Files
UnrealEngineUWP/Engine/Source/Programs/IncludeTool/IncludeTool/Preprocessor.cs

2268 lines
85 KiB
C#
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IncludeTool;
using IncludeTool.Support;
using System.Diagnostics;
using System.IO;
using System.Collections;
using System.Collections.Concurrent;
namespace IncludeTool
{
/// <summary>
/// Exception class for the preprocessor, which contains the file and position of the code causing an error
/// </summary>
class PreprocessorException : Exception
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="Format">Format string, to be passed to String.Format</param>
/// <param name="Args">Optional argument list for the format string</param>
public PreprocessorException(string Format, params object[] Args)
: base(String.Format(Format, Args))
{
}
}
/// <summary>
/// Stores information about a defined preprocessor macro
/// </summary>
class PreprocessorMacro
{
/// <summary>
/// Name of the macro
/// </summary>
public string Name;
/// <summary>
/// Parameter names for the macro. The '...' placeholder is represented by the VariadicParameter string.
/// </summary>
public List<string> Parameters;
/// <summary>
/// Raw list of tokens for this macro
/// </summary>
public List<Token> Tokens;
/// <summary>
/// The parameter name used for the variadic parameter. This appears in place of '...' in the parameters list.
/// </summary>
public const string VariadicParameter = "__VA_ARGS__";
/// <summary>
/// Construct a preprocessor macro
/// </summary>
/// <param name="InName">Name of the macro</param>
/// <param name="InParameters">Parameter list for the macro. Should be null for object macros. Ownership of this list is transferred.</param>
/// <param name="InTokens">Tokens for the macro. Ownership of this list is transferred.</param>
public PreprocessorMacro(string InName, List<string> InParameters, List<Token> InTokens)
{
Name = InName;
Parameters = InParameters;
Tokens = InTokens;
}
/// <summary>
/// Finds the index of a parameter in the parameter list
/// </summary>
/// <param name="Parameter">Parameter name to look for</param>
/// <returns>Index of the parameter, or -1 if it's not found.</returns>
public int FindParameterIndex(string Parameter)
{
for (int Idx = 0; Idx < Parameters.Count; Idx++)
{
if (Parameters[Idx] == Parameter)
{
return Idx;
}
}
return -1;
}
/// <summary>
/// True if the macro is an object macro
/// </summary>
public bool IsObjectMacro
{
get { return Parameters == null; }
}
/// <summary>
/// True if the macro is a function macro
/// </summary>
public bool IsFunctionMacro
{
get { return Parameters != null; }
}
/// <summary>
/// The number of required parameters. For variadic macros, the last parameter is optional.
/// </summary>
public int MinRequiredParameters
{
get { return HasVariableArgumentList? (Parameters.Count - 1) : Parameters.Count; }
}
/// <summary>
/// True if the macro has a variable argument list
/// </summary>
public bool HasVariableArgumentList
{
get { return Parameters.Count > 0 && Parameters[Parameters.Count - 1] == VariadicParameter; }
}
/// <summary>
/// Converts this macro to a string for debugging
/// </summary>
/// <returns>The tokens in this macro</returns>
public override string ToString()
{
StringBuilder Result = new StringBuilder();
Result.AppendFormat("{0}=", Name);
if (Tokens.Count > 0)
{
Result.Append(Tokens[0].Text);
for (int Idx = 1; Idx < Tokens.Count; Idx++)
{
if(Tokens[Idx].HasLeadingSpace)
{
Result.Append(" ");
}
Result.Append(Tokens[Idx].Text);
}
}
return Result.ToString();
}
}
/// <summary>
/// Record for a file context within the preprocessor stack
/// </summary>
class PreprocessorFile
{
/// <summary>
/// Filename as specified when included
/// </summary>
public string FileName;
/// <summary>
/// Reference to the current file
/// </summary>
public FileReference Location;
/// <summary>
/// The workspace file
/// </summary>
public WorkspaceFile WorkspaceFile;
/// <summary>
/// Construct a new preprocessor file from the given name
/// </summary>
/// <param name="FileName">The filename of the new file</param>
public PreprocessorFile(string FileName, WorkspaceFile WorkspaceFile)
{
this.FileName = FileName;
this.Location = WorkspaceFile.Location;
this.WorkspaceFile = WorkspaceFile;
}
/// <summary>
/// Returns the name of this file for visualization in the debugger
/// </summary>
/// <returns>Name of this file</returns>
public override string ToString()
{
return FileName;
}
}
/// <summary>
/// Implementation of a C++ preprocessor. Should be complete and accurate.
/// </summary>
class Preprocessor
{
/// <summary>
/// Flags indicating the state of each nested conditional block in a stack.
/// </summary>
[Flags]
enum BranchState
{
/// <summary>
/// The current conditional branch is active.
/// </summary>
Active = 0x01,
/// <summary>
/// A branch within the current conditional block has been taken. Any #else/#elif blocks will not be taken.
/// </summary>
Taken = 0x02,
/// <summary>
/// The #else directive for this conditional block has been encountered. An #endif directive is expected next.
/// </summary>
Complete = 0x04,
/// <summary>
/// This branch is within an active parent branch
/// </summary>
ParentIsActive = 0x08,
/// <summary>
/// The first condition in this branch was an #if directive (as opposed to an #ifdef or #ifndef directive)
/// </summary>
HasIfDirective = 0x10,
/// <summary>
/// The first condition in this branch was an #if directive (as opposed to an #ifdef or #ifndef directive)
/// </summary>
HasIfdefDirective = 0x20,
/// <summary>
/// The first condition in this branch was an #ifndef directive (as opposed to an #ifdef or #if directive)
/// </summary>
HasIfndefDirective = 0x40,
/// <summary>
/// The branch has an #elif directive
/// </summary>
HasElifDirective = 0x80,
}
/// <summary>
/// A directory to search for include paths
/// </summary>
[DebuggerDisplay("{Location}")]
class IncludeDirectory
{
/// <summary>
/// The directory, as specified as a parameter. We keep the original spelling for formatting the preprocessor output to match MSVC.
/// </summary>
public DirectoryReference Location;
/// <summary>
/// The corresponding workspace directory
/// </summary>
public WorkspaceDirectory WorkspaceDirectory;
}
/// <summary>
/// Stack of conditional branch states
/// </summary>
Stack<BranchState> Branches = new Stack<BranchState>();
/// <summary>
/// Mapping of name to macro definition
/// </summary>
Dictionary<string, PreprocessorMacro> NameToMacro = new Dictionary<string, PreprocessorMacro>();
/// <summary>
/// The stack of included source files
/// </summary>
List<PreprocessorFile> Files = new List<PreprocessorFile>();
/// <summary>
/// Include paths to look in
/// </summary>
List<IncludeDirectory> IncludeDirectories = new List<IncludeDirectory>();
/// <summary>
/// Set of all included files with the #pragma once directive
/// </summary>
HashSet<FileReference> PragmaOnceFiles = new HashSet<FileReference>();
/// <summary>
/// Value of the __COUNTER__ variable
/// </summary>
int Counter;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="Definitions">Definitions passed in on the command-line</param>
/// <param name="IncludePaths">Paths to search when resolving include directives</param>
public Preprocessor(FileReference PreludeFile, IEnumerable<string> Definitions, IEnumerable<DirectoryReference> IncludePaths)
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
{
DateTime Now = DateTime.Now;
AddSingleTokenMacro("__DATE__", TokenType.StringLiteral, String.Format("\"{0} {1,2} {2}\"", Now.ToString("MMM"), Now.Day, Now.Year));
AddSingleTokenMacro("__TIME__", TokenType.StringLiteral, "\"" + Now.ToString("HH:mm:ss") + "\"");
AddSingleTokenMacro("__FILE__", TokenType.StringLiteral, "\"<unknown>\"");
AddSingleTokenMacro("__LINE__", TokenType.NumericLiteral, "-1");
AddSingleTokenMacro("__COUNTER__", TokenType.NumericLiteral, "-1");
AddSingleTokenMacro("CHAR_BIT", TokenType.NumericLiteral, "8"); // Workaround for #include_next not being supported on Linux for limit.h
AddMacro("__has_cpp_attribute", new List<string> { "_" }, new List<Token> { new Token("1", TokenType.NumericLiteral, TokenFlags.None) }); // Workaround for `__has_cpp_attribute` (a core C++ feature) being defined by a 3rd-party library
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
if(PreludeFile != null)
{
TextBuffer PreludeText = TextBuffer.FromFile(PreludeFile.FullName);
Copying //UE4/Dev-Platform to //UE4/Dev-Main (Source: //UE4/Dev-Platform @ 3233741) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== [NOTE: Switch changes have been removed from this list, and will be in a comment in //UE4/Main/Engine/Build/Switch/SwitchChanges.txt] Change 3207431 on 2016/11/22 by Keith.Judge Make VectorSign on XB1 match (incorrect) SSE implementation rather than the reference FPU implementation. Too many things seem to rely on this to change the default behaviour now. #jira UE-36921 #jira UE-38560 Change 3208206 on 2016/11/22 by Josh.Adams - Changed plugins upgrading a content-only project to code-based, even if the modules inside aren't compiled for the current platform. There are issues with runtime not knowing the plugin isn't needed. The proper way is to non-whitelist the platform in the project, not in the modules, if you don't want it to upgrade the project. See the comments in this change for more info. #jira UE-38929 Change 3209137 on 2016/11/23 by Alicia.Cano Add a check to iOS tool chain for exception flag #jira UE-36528 #ios Change 3209296 on 2016/11/23 by Ben.Marsh Always send build failure notifications in Dev-Platform to Will.Fissler@epicgames.com and Owen.Stupka@epicgames.com Change 3211316 on 2016/11/28 by Joe.Barnes Fix some typos Change 3211318 on 2016/11/28 by Joe.Barnes Fix wrong function name in header file. Didn't match actual function name in RenderingThread.cpp Change 3213227 on 2016/11/29 by Dmitry.Rekman Add -fPIC to libwebsockets on Linux. Change 3213463 on 2016/11/29 by Nick.Shin helper build scripts for CentOS 7 Linux (via Docker) LINUX: pull source and compile: zlib openssl libcurl & libwebsockets using [ glibc 2.17 ] & [ gcc 4.8.5 ] Docker creates an image (that is essentially a CentOS box) and runs the build script within that environment (called a container). think of this as a VM -- but waaaaaaay better -- nothing is virtualized -- it's all on the metal. #jira UEPLAT-1246 - Update libWebsockets #jira UEPLAT-1221 - update websocket library #jira UEPLAT-1203 - Add Linux library for libwebsockets #jira UEPLAT-1204 - Rebuild libwebsockets with SSL Change 3213939 on 2016/11/29 by Michael.Trepka Ignore parent widget's geometry scale when showing a popup menu in a separate window #jira UE-38706 Change 3215583 on 2016/11/30 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change 3216345 on 2016/11/30 by Josh.Adams Better fix for poison proxy fix Change 3217106 on 2016/12/01 by Michael.Trepka Fixed a crash caused by an attempt to use FPlayWorldCommands::GlobalPlayWorldActions before it's initialized when showing suppressable warning dialogs in UEditorEngine::UpdateAutoLoadProject() #jira UE-38816 Change 3217223 on 2016/12/01 by Josh.Adams - Fixed some TEXT macro warnings that crept in from IWYU changes Change 3217253 on 2016/12/01 by Dmitry.Rekman Linux: fix GL crash (UE-17649). - Making sure all streams are set up. Fix by Cengiz. Change 3217473 on 2016/12/01 by Daniel.Lamb Fixed a case when we load a map it might not have it's sublevels loaded because it's not treated like a map. #test cook run QAGame Change 3217588 on 2016/12/01 by Peter.Sauerbrei Pull in IPv6 fix Change 3217654 on 2016/12/01 by Michael.Trepka Changed the Mac-specific high DPI code to use system backingScaleFactor only if NSHighResolutionCapable in Info.plist is set to true. This solves the problem with macOS Sierra giving us actual backingScaleFactor values even in low DPI modes. Change 3217873 on 2016/12/01 by Josh.Adams - Added some logging to a tvOS assert, since debugging it right away is tricky Change 3218097 on 2016/12/01 by Josh.Adams - Fixed up the Switch MediaFramework, making editor better, etc - ALso changed PS4 and Xbox plugins to be enabled by default by having two entries in the plugin module for the Factory modules (an editor only entry, and a platform specific runtime entry... this will make it so that UE4Game.exe won't ahve it compiled in, even with it enabled by default) Change 3218133 on 2016/12/01 by Dmitry.Rekman Linux: report server hangs by crashing the hung thread (UE-39164). Change 3218512 on 2016/12/01 by Josh.Adams - Made the MfMedia plugin to be distributable in public builds, since it's for Windows and Xbox Change 3219804 on 2016/12/02 by Dmitry.Rekman Linux: fix project settings crash (UE-38800). - Also submitted as a pull request #2945. Change 3220027 on 2016/12/02 by Nick.Shin plow all physx libs into build NOTE: most browsers will not function - chrome and firefox nightly only works checking this in as per email #jira UE-38323 VehicleTemplate Vehicle does not move in HTML5 Change 3221620 on 2016/12/05 by Joe.Barnes UE-37275 - Temporary workaround for log lines losing carriage returns. Add's a \n when outputting lines if there isn't one at the end. Change 3221689 on 2016/12/05 by Dmitry.Rekman Attempt to change/rename. Change 3221700 on 2016/12/05 by Dmitry.Rekman Another attempt to change renamed file (from Linux). Change 3221731 on 2016/12/05 by Michael.Trepka Added missing initialization for FAvfVideoSampler::MetalTextureCache #jira UE-38689 Change 3221792 on 2016/12/05 by Michael.Trepka Fixed a crash in FMetalDynamicRHI::RHIAsyncReallocateTexture2D for PVRTC2 textures Change 3222675 on 2016/12/05 by Josh.Adams - Removed some resolution setting junk that was recently added to PlatformerGame - settings resolution on AppleTV is bad, it doesn't need to change resolution on non-desktop platforms #jira UE-39188 Change 3223546 on 2016/12/06 by Brent.Pease + Properly set and use the realtime compression for ios. + Reduce unused memory on ios from the precached first buffer + Fix a resource tracking issue that was causing a double free on the sound buffer Change 3223785 on 2016/12/06 by Brent.Pease + Add support for iPhone7 (implemented by peter.sauerbrei, merged in from WEX) #jira ue-38701 Change 3224314 on 2016/12/06 by Chris.Babcock Send OnTargetPlatformChangedSupportedFormats when format changed in Android project settings in editor #jira UE-38361 #ue4 #android Change 3225367 on 2016/12/07 by Josh.Adams - Added FKey::Virtual_Accept and Virtual_Back, which will map to FaceButton Right/Down appropriately based on platform (Switch swaps them) - Made changes to ShooterGame and VehicleGame for Virtual_Accept and Back - Added some icons for ShooterGame, and changed some text blocks to SRichTextBlock to insert the icons Change 3225426 on 2016/12/07 by Chris.Babcock Add missing Android UPL file for binary builds #jira UE-39420 #ue4 #android Change 3225471 on 2016/12/07 by Dmitry.Rekman Update all platforms to C++14. Change 3225525 on 2016/12/07 by Nick.Shin Cook-On-The-Fly for HTML5 - re-enabled: ENetworkFileServerProtocol::NFSP_Http - cleaned up port numbers used with cook-on-the-fly situations - fixed null_ptr in NetworkFileServerHttp.cpp - fix CORS issue with HTML5LaunchHelper (not really needed -- but doesn't hurt to have it in the test server) - finally, the core of the jira issue: o fix serialization bug: do not append zero sized data o fix de-serialization bug: removed double insertion of packet "Marker and Size" header #jira UE-38281 Quicklaunch UFE HTML5 fails to get COTF Header Size Change 3225690 on 2016/12/07 by Dmitry.Rekman Linux: improvements in touch support. - Multiple fingers. - Filtering out "moved" events from the same location. - Consistent logging. (Edigrating 3225194 from Wombat to Dev-Platform) Change 3225868 on 2016/12/07 by Josh.Stoddard Gracefully handle delete without matching new on iOS & Mac #jira UE-39395 Change 3226159 on 2016/12/07 by Omar.Rodriguez UEPLAT-1423 WEX: Improved virtual keyboard for Android * Renamed old virtual keyboard functions by adding "Dialog" suffix to the name * Added new virtual keyboard functions that use InputMethodManager to show/hide keyboard * Hide the virtual keyboard, if shown, onPause * Slate edit box decides which functions to call for showing/hiding keyboard - eventually will be based on command line parameter like in IOS #jira UEPLAT-1423 Change 3226167 on 2016/12/07 by Dmitry.Rekman Allow running as root on ARM. (Edigrating 3204974 to Dev-Platform) Change 3226168 on 2016/12/07 by Dmitry.Rekman Print current CVar value when denying an override. (Based on CL 3205476). Change 3226169 on 2016/12/07 by Dmitry.Rekman Allow enabling sound (if disabled by default). (Based on CL 3205505) Change 3226171 on 2016/12/07 by Dmitry.Rekman Allow running from symlinks. (Edigrating 3205518 to Dev-Platform). Change 3226174 on 2016/12/07 by Dmitry.Rekman Linux: do not init SDL audio (we do not use it anyway). (Based on CL 3205505). Change 3226327 on 2016/12/07 by Nick.Shin fix CIS warning #jira UE-38281 Quicklaunch UFE HTML5 fails to get COTF Header Size Change 3226506 on 2016/12/08 by Dmitry.Rekman Fix one more case-sensitive misspelling (UE-39030). - Submitted as part of PR #2976. Change 3226542 on 2016/12/08 by Dmitry.Rekman Linux: fix weirdness with tesselation in GL4 (UE-32865). - Workaround by CengizT. Proper fix tracked as UE-39489. Change 3226570 on 2016/12/08 by Dmitry.Rekman Fix for ar failing due to too long command line (UE-39009). - Based on PR #2973. Change 3226575 on 2016/12/08 by Dmitry.Rekman Add build-essential to dependencies (UE-39053). - PR #2981 contributed by cpyarger. Change 3227129 on 2016/12/08 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Fixed up a deferred GL error as well Fixed some copyrights of files not in main Change 3227260 on 2016/12/08 by Omar.Rodriguez UE-39140 Projects with iCloud are failing provisioning check when code signing. * Set default value of bEnableCloudKitSupport to False * Set value of get-task-allow to true only on non-distribution builds * Only write out the entitlements file if changes have been made #jira UE-39140 Change 3229312 on 2016/12/09 by Dmitry.Rekman Fix missing responses (UE-39572). - Proper implementation of UE-39009. Change 3230849 on 2016/12/12 by Dmitry.Rekman Linux: fixed Android packaging (UE-39635). - Misspelled case; fixed by JohnHenry Carawon. #jira UE-39635 Change 3231591 on 2016/12/12 by Peter.Sauerbrei fix for splash screen not being turned off by default #jira UE-39591 Change 3231880 on 2016/12/12 by Josh.Adams - Fixing StaticAnalysis warnings, but -enablecodeanalysis stopped working for some reason, and the /Zm thing has hit me really hard, so this is a hopeful checkin for static analysis issues #jira UE-39680 Change 3232816 on 2016/12/13 by Dmitry.Rekman Linux: fix for CEF (UE-39682) - Fix by Cengiz.Terzibas. Change 3232873 on 2016/12/13 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change 3232933 on 2016/12/13 by Josh.Adams - Missed the files that were needed to fix up after merge from main, but didn';t come from main Change 3233066 on 2016/12/13 by Ben.Marsh UBT: Ignore exception if PATH variable contains invalid characters when looking for XGE. Change 3233512 on 2016/12/13 by Ben.Marsh Fix static analysis warnings. [CL 3233813 by Josh Adams in Main branch]
2016-12-13 19:47:16 -05:00
PreprocessorMarkup[] PreludeMarkup = PreprocessorMarkup.ParseArray(PreludeText);
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
foreach(PreprocessorMarkup Markup in PreludeMarkup)
{
ParseMarkup(Markup.Type, Markup.Tokens, Markup.Location.LineIdx);
}
}
foreach(string Definition in Definitions)
{
// Create a token reader for this definition
TokenReader Reader = new TokenReader(TextBuffer.FromString(Definition), TextLocation.Origin);
// Read it into a buffer
List<Token> Tokens = new List<Token>();
while (Reader.MoveNext() && Reader.Current.Text != "\n")
{
Tokens.Add(Reader.Current);
}
// Create the macro for it
if(Tokens.Count == 0 || Tokens[0].Type != TokenType.Identifier)
{
throw new PreprocessorException("Invalid token for define");
}
else if(Tokens.Count == 1)
{
NameToMacro[Tokens[0].Text] = new PreprocessorMacro(Tokens[0].Text, null, new List<Token> { new Token("1", TokenType.NumericLiteral, TokenFlags.None) });
}
else if(Tokens[1].Text == "=")
{
NameToMacro[Tokens[0].Text] = new PreprocessorMacro(Tokens[0].Text, null, Tokens.Skip(2).ToList());
}
else if(Tokens[1].Text != "(")
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
{
throw new PreprocessorException("Expected '=' token for definition on command-line");
}
}
Copying //UE4/Dev-Core to //UE4/Dev-Main (Source: //UE4/Dev-Core @ 3847469) #lockdown Nick.Penwarden #rb none ============================ MAJOR FEATURES & CHANGES ============================ Change 3805828 by Gil.Gribb UE4 - Fixed a bug in the lock free stalling task queue and adjusted a comment. The code is not current used, so this is not actually change the way the code works. Change 3806784 by Ben.Marsh UAT: Remove code to compile UBT when using UE4Build. It should already be compiled as a dependency of UAT. Change 3807549 by Graeme.Thornton Add a cook timer around VerifyCanCookPackage. A licensee reports this taking a lot of time so it'll be good to account for it. Change 3807727 by Graeme.Thornton Unhide the text asset format experimental editor option Change 3807746 by Josh.Engebretson Remove WER from iOS platform Change 3807928 by Robert.Manuszewski When async loading, GC Clusters will be created after packages have been processed to avoid situations where some of the objects that are being added to a cluster haven't been fully loaded yet Change 3808221 by Steve.Robb GitHub #4307 - Made GetModulePtr() thread safe by not using GetModule() ^ I'm not convinced by how much thread-safer this is really, but it's tidier anyway. Change 3809233 by Graeme.Thornton TBA: Misc changes to text asset commandlet - Rename mode to "loadsave" - Add -outputFormat option which can be assigned "text" or "binary" - When saving binary, use a differentiated filename so that source assets aren't overwritten Change 3809518 by Ben.Marsh Remove the outdated UnrealSync automation script. Change 3809643 by Steve.Robb GitHub #4277 : fix bug; FMath::FormatIntToHumanReadable 3rd comma and negative value #jira UE-53037 Change 3809862 by Steve.Robb GitHub #3342 : [FRotator.h] Fix to DecompressAxisFromByte to be more efficient and reflect its intent accurately #jira UE-42593 Change 3811190 by Graeme.Thornton Add support for writing specific log channels to their own files Change 3811197 by Graeme.Thornton Minor updates to output formatting and timing for the text asset commandlet Change 3811257 by Robert.Manuszewski Cluster creation will now be time-sliced Change 3811565 by Steve.Robb Define out non-monolithic module functions. Change 3812561 by Steve.Robb GitHub #3886 : Enable Brace-Initialization for Declaring Variables Incorrect semi-colon search removed after discussion with author. Test added. #jira UE-48242 Change 3812864 by Steve.Robb Removal of some unproven code which was supposed to fix hot reloading BP class functions in plugins. See: https://udn.unrealengine.com/questions/376978/aitask-blueprint-nodes-disappear-when-their-module.html #jira UE-53089 Change 3820358 by Ben.Marsh PR #4358: Incredibuild use ShowAgent by default (Contributed by projectgheist) Change 3822594 by Ben.Marsh UAT: Improvements to log file handling. - Always create log files in the final location, rather than writing to a temp directory and copying in later. - Now supports -Verbose and -VeryVerbose for increasing log verbosity, rather than -Verbose=XXX. - Keep a backlog of log output before the log system is initialized, and flush it to the log file once it is. - Allow buildmachines to specify the uebp_FinalLogFolder environment variable, which is used to form paths for display. When build machines copy log files elsewhere after UAT finishes (eg. a network share), this allows error messages to display the right location. Change 3823695 by Ben.Marsh UGS: Fix issue where precompiled binaries would not be shown as available for a change until scrolling the last submitted code change into the buffer (other symptoms, like de-focussing the main window would cause it to go back to an unavailable state, since the changes buffer was shrunk). Now always queries changes up to the last change for which zipped binaries are available. Change 3823845 by Ben.Marsh UBT: Exclude C# projects for unsupported platforms when generating project files. Change 3824180 by Ben.Marsh UGS: Add an option to show changes by build machines, and move the "only show reviewed" option in there too (Options > Show Changes). #jira Change 3825777 by Steve.Robb Fix to return value of StringToBytes. Change 3825810 by Ben.Marsh UBT: Reduce length of include paths for MSVC toolchain. Change 3825822 by Robert.Manuszewski Optimized PIE lazy pointer fixup. Should be up to 8x faster now. Change 3826734 by Ben.Marsh Remove code to disable TextureFormatAndroid on Linux. It seems to be an editor dependency. Change 3827730 by Steve.Robb Try to avoid decltype(auto) if it's not supported. See: https://udn.unrealengine.com/questions/395644/build-417-with-c11-on-linux-ttuple-errors.html Change 3827745 by Steve.Robb Initializer list support for TMap. Change 3827770 by Steve.Robb GitHub #4399 : Added a CONSTEXPR qualifiers to FVariant::GetType() #jira UE-53813 Change 3829189 by Ben.Marsh UBT: Now always writes a minimal log file. By default, just contains the regular console output and any reasons why actions are outdated and needed to be executed. UAT directs child UBT instances to output logs into its own log folder, so that build machines can save them off. Change 3830444 by Steve.Robb BuildVersion and ModuleManifest moved to Core, and parsing of these files reimplemented to avoid a JSON library. This should be revisited when Core has its own JSON library. Change 3830718 by Ben.Marsh Fix incorrect group name being returned by FStatNameAndInfo::GetGroupName() for stat groups. The editor populates the viewport stats list by calling this for every registered stat and stat group (via FLevelViewportCommands::HandleNewStatGroup). The menu entry attempts to show the stat name with STAT_XXX stripped from the start as the menu item label, with the free-form text description as a tooltip. For stat groups, the it would previously just return the stat group name as "Groups" (due to the raw naming convention of "//Groups//STATGROUP_Foo//..."). Since this didn't match the expected naming convention in FLevelViewportCommands::HandleNewStat (ie. STAT_XXX or STATGROUP_XXX), it would fail to add it. When the first actual stat belonging to that group is added, it would add a menu entry for the group based on that, but the stat description no longer makes sense as a tooltip for the group. As a result, all the editor tooltips were junk. #jira UE-53845 Change 3831064 by Ben.Marsh Fix log file contention when spawning UBT recursively. Change 3832654 by Ben.Marsh UGS: Fix error panel not being selected when opened, and weird alignment/color issues on it. Change 3832680 by Ben.Marsh UGS: Fix failing to detect workspace if synced to a different stream. Seems to be a regression caused by recent P4D upgrade. Change 3832695 by Ben.Marsh UGS: Invert the options in the 'Show Changes' submenu for simplicity. Change 3833528 by Ben.Marsh UAT: Script to rewrite source files with public include paths relative to the 'Public' folder. Usage is: RebasePublicIncludePaths -UpdateDir=<Dir> [-Project=<Dir>] [-Write]. Change 3833543 by Ben.Marsh UBT: Allow targets to opt-out of having public include paths added for every dependent module. This reduces the command line length when building a target, which has recently become a problem with larger games (due to Microsoft's compiler embedding the command line into each object file, with a maximum length of 64kb). All engine modules are compiled with this enabled; games may opt into it by setting bLegacyPublicIncludePaths = false; from their .target.cs, as may individual modules. Change 3834354 by Robert.Manuszewski Archetype pointer will now be cached to avoid locking the object tables when acquiring its info. It should also be faster this way regardless of any locks. #jira UE-52035 Change 3834400 by Robert.Manuszewski Fixing crash on exit caused by cached archetypes not being cleaned up before static exit cleanup. #jira UE-52035 Change 3834947 by Steve.Robb USE_FORMAT_STRING_TYPE_CHECKING removed from FMsg::Logf and FMsg::Logf_Internal. Change 3835004 by Ben.Marsh Fix code that relies on dubious behavior of requiring referenced "include path only" modules having their _API macros set to be empty, even if the module is actually implemented in a separate DLL. Change 3835340 by Ben.Marsh Fix errors making installed build from directories with spaces in the name. Change 3835972 by Ben.Marsh UBT: Improved diagnostic message for targets which don't need a version file. Change 3836019 by Ben.Marsh UBT: Fix warnings caused by defining linkage macros for third party libraries. Change 3836269 by Ben.Marsh Fix message box larger than the screen height being created when a large number of modules are incompatible on startup. Change 3836543 by Ben.Marsh Enable SoundMod plugin on Linux, since it's already supported through the editor. Change 3836546 by Ben.Marsh PR #4412: fix type mismatch (Contributed by nakapon) Change 3836805 by Ben.Marsh Fix commandlet to compile marketplace plugins. Change 3836829 by Ben.Marsh UBT: Fix ability to precompile plugins from installed engine builds. Change 3837036 by Ben.Marsh UBT: Write the previous and new contents of intermediate files to the log if they change. Makes it easier to debug unexpected rebuilds. Change 3837037 by Ben.Marsh UBT: Fix engine modules having inconsistent definitions depending on whether modules are only referenced for their include paths vs being linked into a binary (due to different _API macro). Change 3837040 by Ben.Marsh UBT: Remove code that initializes members in ModuleRules and TargetRules objects before the constructor is run. This is no longer necessary, now that the backwards-compatible default constructors have been removed. Change 3837247 by Ben.Marsh UBT: Remove UELinkerFixups module, now that plugins and precompiled modules do not require hacks to force initialization (since they're linked in as object files). Encryption and signing keys are now set via macros expanded from the IMPLEMENT_PRIMARY_GAME_MODULE macro, via project-specific macros added in the TargetRules constructor. Change 3837262 by Ben.Marsh UBT: Set whether a module is an engine module or not via a default value for the rules assembly. All non-program engine and enterprise modules are created with this flag set to true; program targets and modules are now created from a different assembly that sets it to false. This removes hacks from UEBuildModule needed to adjust behavior for different module types based on the directory containing the module. Also add a bUseBackwardsCompatibleDefaults flag to the TargetRules class, also initialized to a default value from a setting passed to the RulesAssembly constructor. This controls whether modules created for the target should be configured to allow breaking changes to default settings, and is set to false for all engine targets, and true for all project targets. Change 3837343 by Ben.Marsh UBT: Remove the OverrideExecutableFileExtension target property. Change the only current use for this (the MayaLiveLinkPlugin target) to use a post build step to copy the file instead. Change 3837356 by Ben.Marsh Fix invalid character encodings. Change 3837727 by Graeme.Thornton UnrealPak: KeyGenerator: Only generate prime table when required, not all the time Change 3837823 by Ben.Marsh UBT: Output warnings and errors when compiling module rules assembly in a way that allows them to be double-clicked in the Visual Studio output window. Change 3837831 by Graeme.Thornton UBT: When parsing crypto settings, always load legacy data first, then allow the new system to override it. Provides the same key backwards compatibility that the editor settings class gives Change 3837857 by Robert.Manuszewski PR #4404: Make FGCArrayPool singleton global instead of per-CU (Contributed by mhutch) Change 3837943 by Robert.Manuszewski PR #4405: Fix FGarbageCollectionTracer (Contributed by mhutch) Change 3838451 by Ben.Marsh UBT: Fix exceptions thrown on a background thread while caching C++ includes not being caught and logged correctly. Now captures exceptions and re-throws on the main thread. #jira UE-53996 Change 3839519 by Ben.Marsh UBT: Simplify configuring bPrecompile and bUsePrecompile settings for modules. Each rules assembly can now be configured as installed, which defaults the module rules it creates to use precompiled data. Change 3843790 by Graeme.Thornton UnrealPak: Log the size of all encrypted data Change 3844258 by Ben.Marsh Fix plugin compile failure when created via new plugin wizard. Passing -plugin on the command line is unnecessary, and is now reserved for packaging external plugins for the marketplace. Also extend the length of time that the error toast stays visible, and don't delete the plugin on failure. #jira UE-54157 Change 3845796 by Ben.Marsh Workaround for slow performance of String.EndsWith() on Mono. Change 3845823 by Ben.Marsh Fix case sensitive matching of platform names in -TargetPlatform=X argument to BuildCookRun. #jira UE-54123 Change 3845901 by Arciel.Rekman Linux: fix crash due to lambda lifetime issues (UE-54040). - The lambda goes out of scope in FBufferVisualizationMenuCommands::CreateVisualizationCommands, crashing the editor if compiled with a recent clang (5.0+). (Edigrating 3819174 to Dev-Core) Change 3846439 by Ben.Marsh Revert CL 3822742 to always call Process.WaitForExit(). The Android target platform module in the editor spawns ADB.EXE, which inherits the editor's stdout/stderr handles and forks itself. Process.WaitForExit() waits for EOF on those pipes, which never occurs because the forked process never terminates. Proper fix is probably to have the engine explicitly duplicate stdout/stderr handles for new pipes to output process, but too risky before copying up to Main. Change 3816608 by Ben.Marsh UBT: Use DirectoryReference objects for all include paths. Change 3816954 by Ben.Marsh UBT: Remove bIncludeDependentLibrariesInLibrary option. This is not widely supported by platform toolchains, and is not used anywhere. Change 3816986 by Ben.Marsh UBT: Remove UEBuildBinaryConfig; UEBuildBinary objects are now just created directly. Change 3816991 by Ben.Marsh UBT: Deprecate PlatformSpecificDynamicallyLoadedModules. We no longer have any special behavior for these modules. Change 3823090 by Ben.Marsh UAT: Improve logging for child UAT instances. - Calling RunUAT now requires an identifier for prefixing into the parent log, which is also used to determine the name of the log folder. - Stdout is no longer written to its own output file, since it's written to the parent stdout, the parent log file, and the child log file anyway. - Log folders for child UAT instances are left intact, rather than being copied to the parent folder. The derived names for the copied names were confusing and hard to read. - Output from UAT is no longer returned as a string. It should not be parsed anyway (but may be huge!). ProcessResult now supports running without capturing output. Change 3826082 by Ben.Marsh UBT: Add a check to make sure that all modules that are precompiled are correctly marked to enable it, even if they are part of the build target. Change 3827025 by Ben.Marsh UBT: Move the compile output directory into a property on the module, and explicitly pass it to the toolchain when compiling. Change 3829927 by James.Hopkin Made HTTP interface const correct Change 3833533 by Ben.Marsh Rewrite engine source files to base include paths relative to the "Public" directory. This allows reducing the number of public include paths that have to be added for engine modules. Change 3835826 by Ben.Marsh UBT: Precompiled targets now generate a separate manifest for each precompiled module, rather than adding object files to a library. This fixes issues where object files from static libraries would not be linked into a target if a symbol in them was not referenced. Change 3835969 by Ben.Marsh UBT: Fix cases where text is being written directly to the console rather than via logging functions. Change 3837777 by Steve.Robb Format string type checking added to FOutputDevice::Logf. Fixes for those. Change 3838569 by Steve.Robb Algo moved up a folder. [CL 3847482 by Ben Marsh in Main branch]
2018-01-20 11:19:29 -05:00
foreach(DirectoryReference IncludePath in IncludePaths)
{
if(Directory.Exists(IncludePath.FullName))
{
IncludeDirectories.Add(new IncludeDirectory() { Location = IncludePath, WorkspaceDirectory = Workspace.GetDirectory(IncludePath) });
}
}
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
}
/// <summary>
/// Returns the path to the current file
/// </summary>
public PreprocessorFile CurrentFile
{
get { return Files[Files.Count - 1]; }
}
/// <summary>
/// Returns an array consisting of the current include stack
/// </summary>
/// <returns>Array of the current include stack</returns>
public FileReference[] CaptureIncludeStack()
{
return Files.Select(x => x.Location).ToArray();
}
/// <summary>
/// Determines if the branch that the preprocessor is in is active
/// </summary>
/// <returns>True if the branch is active, false otherwise</returns>
public bool IsBranchActive()
{
return Branches.Count == 0 || Branches.Peek().HasFlag(BranchState.Active);
}
/// <summary>
/// Push a file onto the preprocessor stack
/// </summary>
/// <param name="File">The file to add</param>
public bool PushFile(PreprocessorFile File)
{
if(!PragmaOnceFiles.Contains(File.Location))
{
Files.Add(File);
if (File.WorkspaceFile.ReadSourceFile().HasHeaderGuard)
{
PragmaOnceFiles.Add(File.Location);
}
return true;
}
return false;
}
/// <summary>
/// Remove the topmost file from the preprocessor stack
/// </summary>
public void PopFile()
{
Files.RemoveAt(Files.Count - 1);
}
/// <summary>
/// Format a #line directive at the given line
/// </summary>
/// <param name="CurrentLine">The current line being parsed</param>
/// <returns></returns>
string FormatLineDirective(int CurrentLine)
{
return String.Format("#line {0} \"{1}\"", CurrentLine, CurrentFile.FileName.Replace("\\", "\\\\"));
}
/// <summary>
/// Validate and add a macro using the given parameter and token list
/// </summary>
/// <param name="Name">Name of the macro</param>
/// <param name="Parameters">Parameter list for the macro</param>
/// <param name="Tokens">List of tokens</param>
void AddMacro(string Name, List<string> Parameters, List<Token> Tokens)
{
if(Tokens.Count == 0)
{
Tokens.Add(new Token("", TokenType.Placemarker, TokenFlags.None));
}
else
{
if(Tokens[0].HasLeadingSpace)
{
Tokens[0] = new Token(Tokens[0].Text, Tokens[0].Type, Tokens[0].Flags & ~TokenFlags.HasLeadingSpace);
}
if (Tokens[0].Text == "##" || Tokens[Tokens.Count - 1].Text == "##")
{
throw new PreprocessorException("Invalid use of concatenation at start or end of token sequence");
}
if (Parameters == null || Parameters.Count == 0 || Parameters[Parameters.Count - 1] != PreprocessorMacro.VariadicParameter)
{
if(Tokens.Any(x => x.Text == PreprocessorMacro.VariadicParameter))
{
throw new PreprocessorException("Invalid reference to {0}", PreprocessorMacro.VariadicParameter);
}
}
}
NameToMacro[Name] = new PreprocessorMacro(Name, Parameters, Tokens);
}
/// <summary>
/// Set a predefined macro to a given value
/// </summary>
/// <param name="Name">Name of the macro</param>
/// <param name="Type">Type of the macro token</param>
/// <param name="Value">Value of the macro</param>
/// <returns>The created macro</returns>
void AddSingleTokenMacro(string Name, TokenType Type, string Value)
{
Token Token = new Token(Value, Type, TokenFlags.None);
PreprocessorMacro Macro = new PreprocessorMacro(Name, null, new List<Token> { Token });
NameToMacro[Name] = Macro;
}
/// <summary>
/// Tries to resolve an include
/// </summary>
/// <param name="Tokens">List of include tokens</param>
/// <returns>The resolved include file</returns>
public PreprocessorFile ResolveInclude(List<Token> Tokens, int CurrentLine)
{
// Expand macros in the given tokens
List<Token> ExpandedTokens = new List<Token>();
ExpandMacros(Tokens, ExpandedTokens, false, CurrentLine);
// Expand any macros in them and resolve it
string IncludeToken = Token.Format(ExpandedTokens).Replace("//", "/");
if (IncludeToken.Length >= 2 && IncludeToken.StartsWith("\"") && IncludeToken.EndsWith("\""))
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
{
return ResolveQuotedInclude(IncludeToken.Substring(1, IncludeToken.Length - 2));
}
else if(IncludeToken.StartsWith("<") && IncludeToken.EndsWith(">"))
{
return ResolveSystemInclude(IncludeToken.Substring(1, IncludeToken.Length - 2));
}
else
{
throw new PreprocessorException("Couldn't resolve include '{0}'", IncludeToken);
}
}
/// <summary>
/// Try to resolve an quoted include against the list of include directories. Uses search order described by https://msdn.microsoft.com/en-us/library/36k2cdd4.aspx.
/// </summary>
/// <param name="IncludeText">The path appearing in quotes in an #include directive</param>
/// <returns>The resolved file</returns>
public PreprocessorFile ResolveQuotedInclude(string IncludeText)
{
// If it's an absolute path, return it immediately
if(Path.IsPathRooted(IncludeText))
{
WorkspaceFile WorkspaceFile = Workspace.GetFile(new FileReference(IncludeText));
return new PreprocessorFile(IncludeText, WorkspaceFile);
}
// Otherwise search through the open file directories
string[] RelativePathFragments = IncludeText.Split('/', '\\');
for(int Idx = Files.Count - 1; Idx >= 0; Idx--)
{
WorkspaceFile WorkspaceFile;
if(TryResolveRelativePath(Files[Idx].WorkspaceFile.Directory, RelativePathFragments, out WorkspaceFile))
{
string FileName = Path.Combine(Utility.RemoveRelativePaths(Path.GetDirectoryName(Files[Idx].FileName)).ToLowerInvariant(), IncludeText);
return new PreprocessorFile(FileName, WorkspaceFile);
}
}
// Otherwise fall back to the system search
return ResolveSystemInclude(IncludeText, RelativePathFragments);
}
/// <summary>
/// Try to resolve a system include against the list of include directories
/// </summary>
/// <param name="IncludeText">The path appearing in quotes in an #include directive</param>
/// <returns>The resolved file</returns>
public PreprocessorFile ResolveSystemInclude(string IncludeText)
{
// If it's an absolute path, return it immediately
if(Path.IsPathRooted(IncludeText))
{
WorkspaceFile WorkspaceFile = Workspace.GetFile(new FileReference(IncludeText));
return new PreprocessorFile(IncludeText, WorkspaceFile);
}
// Otherwise try to resolve a relative path
return ResolveSystemInclude(IncludeText, IncludeText.Split('/', '\\'));
}
/// <summary>
/// Try to resolve a system include against the list of include directories
/// </summary>
/// <param name="IncludeText">The path appearing in quotes in an #include directive</param>
/// <param name="RelativePathFragments">The components of a relative path, without directory separators</param>
/// <returns>The resolved file</returns>
public PreprocessorFile ResolveSystemInclude(string IncludeText, string[] RelativePathFragments)
{
foreach(IncludeDirectory IncludeDirectory in IncludeDirectories)
{
WorkspaceFile WorkspaceFile;
if(TryResolveRelativePath(IncludeDirectory.WorkspaceDirectory, RelativePathFragments, out WorkspaceFile))
{
string FileName = Path.Combine(IncludeDirectory.Location.FullName, IncludeText);
return new PreprocessorFile(FileName, WorkspaceFile);
}
}
throw new PreprocessorException("Couldn't resolve include {0}", IncludeText);
}
/// <summary>
/// Tries to get a file at the relative path from a base directory
/// </summary>
/// <param name="BaseDirectory">The base directory to search from</param>
/// <param name="RelativePathFragments">Fragments of the relative path to follow</param>
/// <param name="Result">The file that was found, if successful</param>
/// <returns>True if the file was located</returns>
static bool TryResolveRelativePath(WorkspaceDirectory BaseDirectory, string[] RelativePathFragments, out WorkspaceFile Result)
{
WorkspaceDirectory NextDirectory = BaseDirectory;
for(int Idx = 0; Idx < RelativePathFragments.Length - 1; Idx++)
{
if(!NextDirectory.TryGetDirectory(RelativePathFragments[Idx], out NextDirectory))
{
Result = null;
return false;
}
}
return NextDirectory.TryGetFile(RelativePathFragments[RelativePathFragments.Length - 1], out Result);
}
/// <summary>
/// Parse a marked up directive from a file
/// </summary>
/// <param name="Type">The markup type</param>
/// <param name="Tokens">Tokens for the directive</param>
/// <param name="CurrentLine">The line number being processed</param>
public void ParseMarkup(PreprocessorMarkupType Type, List<Token> Tokens, int CurrentLine)
{
switch(Type)
{
case PreprocessorMarkupType.Include:
throw new PreprocessorException("Include directives should be handled by the caller.");
case PreprocessorMarkupType.Define:
ParseDefineDirective(Tokens);
break;
case PreprocessorMarkupType.Undef:
ParseUndefDirective(Tokens);
break;
case PreprocessorMarkupType.If:
ParseIfDirective(Tokens, CurrentLine);
break;
case PreprocessorMarkupType.Ifdef:
ParseIfdefDirective(Tokens);
break;
case PreprocessorMarkupType.Ifndef:
ParseIfndefDirective(Tokens);
break;
case PreprocessorMarkupType.Elif:
ParseElifDirective(Tokens, CurrentLine);
break;
case PreprocessorMarkupType.Else:
ParseElseDirective(Tokens);
break;
case PreprocessorMarkupType.Endif:
ParseEndifDirective(Tokens);
break;
case PreprocessorMarkupType.Pragma:
ParsePragmaDirective(Tokens);
break;
}
}
/// <summary>
/// Read a macro definition
/// </summary>
/// <param name="Tokens">List of tokens in the directive</param>
public void ParseDefineDirective(List<Token> Tokens)
{
if(IsBranchActive())
{
// Check there's a name token
if(Tokens.Count < 1 || Tokens[0].Type != TokenType.Identifier || Tokens[0].Text == "defined")
{
throw new PreprocessorException("Invalid macro name");
}
// Read the macro name
string Name = Tokens[0].Text;
int TokenIdx = 1;
// Read the macro parameter list, if there is one
List<string> Parameters = null;
if (TokenIdx < Tokens.Count && !Tokens[TokenIdx].HasLeadingSpace && Tokens[TokenIdx].Type == TokenType.LeftParen)
{
Parameters = new List<string>();
if(++TokenIdx == Tokens.Count)
{
throw new PreprocessorException("Unexpected end of macro parameter list");
}
if(Tokens[TokenIdx].Type != TokenType.RightParen)
{
for(;;TokenIdx++)
{
// Check there's enough tokens left for a parameter name, plus ',' or ')'
if(TokenIdx + 2 > Tokens.Count)
{
throw new PreprocessorException("Unexpected end of macro parameter list");
}
// Check it's a valid name, and add it to the list
Token NameToken = Tokens[TokenIdx++];
if(NameToken.Text == "...")
{
if(Tokens[TokenIdx].Type != TokenType.RightParen)
{
throw new PreprocessorException("Variadic macro arguments must be last in list");
}
else
{
NameToken = new Token(PreprocessorMacro.VariadicParameter, TokenType.Identifier, NameToken.Flags & TokenFlags.HasLeadingSpace);
}
}
else
{
if (NameToken.Type != TokenType.Identifier || NameToken.Text == PreprocessorMacro.VariadicParameter)
{
throw new PreprocessorException("Invalid preprocessor token: {0}", NameToken);
}
if (Parameters.Contains(NameToken.Text))
{
throw new PreprocessorException("'{0}' has already been used as an argument name", NameToken);
}
}
Parameters.Add(NameToken.Text);
// Read the separator
Token SeparatorToken = Tokens[TokenIdx];
if (SeparatorToken.Type == TokenType.RightParen)
{
break;
}
if (SeparatorToken.Type != TokenType.Comma)
{
throw new PreprocessorException("Expected ',' or ')'");
}
}
}
TokenIdx++;
}
// Read the macro tokens
AddMacro(Name, Parameters, Tokens.Skip(TokenIdx).ToList());
}
}
/// <summary>
/// Parse an #undef directive
/// </summary>
/// <param name="Tokens">List of tokens in the directive</param>
/// <param name="CurrentLine">The line number being processed</param>
public void ParseUndefDirective(List<Token> Tokens)
{
if(IsBranchActive())
{
// Check there's a name token
if (Tokens.Count != 1)
{
throw new PreprocessorException("Expected a single token after #undef");
}
// Remove the macro from the list of definitions
NameToMacro.Remove(Tokens[0].Text);
}
}
/// <summary>
/// Parse an #if directive
/// </summary>
/// <param name="Tokens">List of tokens in the directive</param>
public void ParseIfDirective(List<Token> Tokens, int CurrentLine)
{
BranchState State = BranchState.HasIfDirective;
if (IsBranchActive())
{
// The new branch is within an active branch
State |= BranchState.ParentIsActive;
// Read a line into the buffer and expand the macros in it
List<Token> ExpandedTokens = new List<Token>();
ExpandMacros(Tokens, ExpandedTokens, true, CurrentLine);
// Evaluate the condition
long Result = PreprocessorExpression.Evaluate(ExpandedTokens);
if (Result != 0)
{
State |= BranchState.Active | BranchState.Taken;
}
}
Branches.Push(State);
}
/// <summary>
/// Parse an #ifdef directive
/// </summary>
/// <param name="Tokens">List of tokens in the directive</param>
public void ParseIfdefDirective(List<Token> Tokens)
{
BranchState State = BranchState.HasIfdefDirective;
if (IsBranchActive())
{
// The new branch is within an active branch
State |= BranchState.ParentIsActive;
// Make sure there's only one token
if(Tokens.Count != 1 || Tokens[0].Type != TokenType.Identifier)
{
throw new PreprocessorException("Missing or invalid macro name for #ifdef directive");
}
// Check if the macro is defined
if(NameToMacro.ContainsKey(Tokens[0].Text))
{
State |= BranchState.Active | BranchState.Taken;
}
}
Branches.Push(State);
}
/// <summary>
/// Parse an #ifndef directive
/// </summary>
/// <param name="Tokens">List of tokens for this directive</param>
public void ParseIfndefDirective(List<Token> Tokens)
{
BranchState State = BranchState.HasIfndefDirective;
if (IsBranchActive())
{
// The new branch is within an active branch
State |= BranchState.ParentIsActive;
// Make sure there's only one token
if (Tokens.Count != 1 || Tokens[0].Type != TokenType.Identifier)
{
throw new PreprocessorException("Missing or invalid macro name for #ifndef directive");
}
// Check if the macro is defined
if(!NameToMacro.ContainsKey(Tokens[0].Text))
{
State |= BranchState.Active | BranchState.Taken;
}
}
Branches.Push(State);
}
/// <summary>
/// Parse an #elif directive
/// </summary>
/// <param name="Tokens">List of tokens for this directive</param>
/// <param name="CurrentLine">The line number being processed</param>
public void ParseElifDirective(List<Token> Tokens, int CurrentLine)
{
// Check we're in a branch, and haven't already read an #else directive
if (Branches.Count == 0)
{
throw new PreprocessorException("#elif directive outside conditional block");
}
if (Branches.Peek().HasFlag(BranchState.Complete))
{
throw new PreprocessorException("#elif directive cannot appear after #else");
}
// Pop the current branch state at this depth, so we can test against whether the parent state is enabled
BranchState State = (Branches.Pop() | BranchState.HasElifDirective) & ~BranchState.Active;
if (IsBranchActive())
{
// Read a line into the buffer and expand the macros in it
List<Token> ExpandedTokens = new List<Token>();
ExpandMacros(Tokens, ExpandedTokens, true, CurrentLine);
// Check we're at the end of a conditional block
if (!State.HasFlag(BranchState.Taken))
{
long Result = PreprocessorExpression.Evaluate(ExpandedTokens);
if (Result != 0)
{
State |= BranchState.Active | BranchState.Taken;
}
}
}
Branches.Push(State);
}
/// <summary>
/// Parse an #else directive
/// </summary>
/// <param name="Tokens">List of tokens in the directive</param>
public void ParseElseDirective(List<Token> Tokens)
{
// Make sure there's nothing else on the line
if (Tokens.Count > 0)
{
throw new PreprocessorException("Garbage after #else directive");
}
// Check we're in a branch, and haven't already read an #else directive
if (Branches.Count == 0)
{
throw new PreprocessorException("#else directive without matching #if directive");
}
if (Branches.Peek().HasFlag(BranchState.Complete))
{
throw new PreprocessorException("Only one #else directive can appear in a conditional block");
}
// Check whether to take this branch, but only allow activating if the parent state is active.
BranchState State = Branches.Pop() & ~BranchState.Active;
if(IsBranchActive() && !State.HasFlag(BranchState.Taken))
{
State |= BranchState.Active | BranchState.Taken;
}
Branches.Push(State | BranchState.Complete);
}
/// <summary>
/// Parse an #endif directive
/// </summary>
/// <param name="Tokens">List of tokens in the directive</param>
public void ParseEndifDirective(List<Token> Tokens)
{
// Check we're in a branch
if (Branches.Count == 0)
{
throw new PreprocessorException("#endif directive without matching #if/#ifdef/#ifndef directive");
}
// Pop the branch off the stack
Branches.Pop();
}
/// <summary>
/// Parse a #pragma directive
/// </summary>
/// <param name="Tokens">List of tokens in the directive</param>
public void ParsePragmaDirective(List<Token> Tokens)
{
if(IsBranchActive())
{
if(Tokens.Count == 1 && Tokens[0].Text == "once")
{
PragmaOnceFiles.Add(CurrentFile.Location);
}
}
}
/// <summary>
/// Expand macros in the given sequence.
/// </summary>
/// <param name="InputTokens">Sequence of input tokens</param>
/// <param name="OutputTokens">List to receive the expanded tokens</param>
/// <param name="bIsConditional">Whether a conditional expression is being evaluated (and 'defined' expressions are valid)</param>
public void ExpandMacros(IEnumerable<Token> InputTokens, List<Token> OutputTokens, bool bIsConditional, int CurrentLine)
{
List<PreprocessorMacro> IgnoreMacros = new List<PreprocessorMacro>();
ExpandMacrosRecursively(InputTokens, OutputTokens, bIsConditional, CurrentLine, IgnoreMacros);
}
/// <summary>
/// Expand macros in the given sequence, ignoring previously expanded macro names from a list.
/// </summary>
/// <param name="InputTokens">Sequence of input tokens</param>
/// <param name="OutputTokens">List to receive the expanded tokens</param>
/// <param name="bIsConditional">Whether a conditional expression is being evaluated (and 'defined' expressions are valid)</param>
/// <param name="CurrentLine">The current line being expanded which can return the current line at any point</param>
/// <param name="IgnoreMacros">List of macros to ignore</param>
void ExpandMacrosRecursively(IEnumerable<Token> InputTokens, List<Token> OutputTokens, bool bIsConditional, int CurrentLine, List<PreprocessorMacro> IgnoreMacros)
{
IEnumerator<Token> InputEnumerator = InputTokens.GetEnumerator();
if(InputEnumerator.MoveNext())
{
for(;;)
{
if(!ReadExpandedToken(InputEnumerator, OutputTokens, bIsConditional, () => CurrentLine, IgnoreMacros))
{
break;
}
}
}
}
/// <summary>
/// Merges an optional leading space flag into the given token (recycling the original token if possible).
/// </summary>
/// <param name="Token">The token to merge a leading space into</param>
/// <param name="bHasLeadingSpace">The leading space flag</param>
/// <returns>New token with the leading space flag set, or the existing token</returns>
Token MergeLeadingSpace(Token Token, bool bHasLeadingSpace)
{
Token Result = Token;
if(bHasLeadingSpace && !Result.HasLeadingSpace)
{
Result = new Token(Result.Text, Result.Type, Result.Flags | TokenFlags.HasLeadingSpace);
}
return Result;
}
/// <summary>
/// Read a token from an enumerator and substitute it if it's a macro or 'defined' expression (reading more tokens as necessary to complete the expression).
/// </summary>
/// <param name="InputEnumerator">The enumerator of input tokens</param>
/// <param name="OutputTokens">List to receive the expanded tokens</param>
/// <param name="bIsConditional">Whether a conditional expression is being evaluated (and 'defined' expressions are valid)</param>
/// <param name="CurrentLine">Callback which can return the current line at any point</param>
/// <param name="IgnoreMacros">List of macros to ignore</param>
/// <returns>Result from calling the enumerator's MoveNext() method</returns>
bool ReadExpandedToken(IEnumerator<Token> InputEnumerator, List<Token> OutputTokens, bool bIsConditional, Func<int> CurrentLine, List<PreprocessorMacro> IgnoreMacros)
{
// Capture the first token, then move to the next
OutputTokens.Add(InputEnumerator.Current);
bool bMoveNext = InputEnumerator.MoveNext();
// If it's an identifier, try to resolve it as a macro
if (OutputTokens[OutputTokens.Count - 1].Text == "defined" && bIsConditional)
{
// Remove the 'defined' keyword
OutputTokens.RemoveAt(OutputTokens.Count - 1);
// Make sure there's another token
if (!bMoveNext)
{
throw new PreprocessorException("Invalid syntax for 'defined' expression");
}
// Check for the form 'defined identifier'
Token NameToken;
if(InputEnumerator.Current.Type == TokenType.Identifier)
{
NameToken = InputEnumerator.Current;
}
else
{
// Otherwise assume the form 'defined ( identifier )'
if(InputEnumerator.Current.Type != TokenType.LeftParen || !InputEnumerator.MoveNext() || InputEnumerator.Current.Type != TokenType.Identifier)
{
throw new PreprocessorException("Invalid syntax for 'defined' expression");
}
NameToken = InputEnumerator.Current;
if(!InputEnumerator.MoveNext() || InputEnumerator.Current.Type != TokenType.RightParen)
{
throw new PreprocessorException("Invalid syntax for 'defined' expression");
}
}
// Insert a token for whether it's defined or not
OutputTokens.Add(new Token(NameToMacro.ContainsKey(NameToken.Text)? "1" : "0", TokenType.NumericLiteral, TokenFlags.None));
bMoveNext = InputEnumerator.MoveNext();
}
else
{
// Repeatedly try to expand the last token into the list
while(OutputTokens[OutputTokens.Count - 1].Type == TokenType.Identifier && !OutputTokens[OutputTokens.Count - 1].Flags.HasFlag(TokenFlags.DisableExpansion))
{
// Try to get a macro for the current token
PreprocessorMacro Macro;
if (!NameToMacro.TryGetValue(OutputTokens[OutputTokens.Count - 1].Text, out Macro) || IgnoreMacros.Contains(Macro))
{
break;
}
if(Macro.IsFunctionMacro && (!bMoveNext || InputEnumerator.Current.Type != TokenType.LeftParen))
{
break;
}
// Remove the macro name from the output list
bool bHasLeadingSpace = OutputTokens[OutputTokens.Count - 1].HasLeadingSpace;
OutputTokens.RemoveAt(OutputTokens.Count - 1);
// Save the initial number of tokens in the list, so we can tell if it expanded
int NumTokens = OutputTokens.Count;
// If it's an object macro, expand it immediately into the output buffer
if(Macro.IsObjectMacro)
{
// Expand the macro tokens into the output buffer
ExpandObjectMacro(Macro, OutputTokens, bIsConditional, CurrentLine(), IgnoreMacros);
}
else
{
// Read balanced token for argument list
List<Token> ArgumentTokens = new List<Token>();
bMoveNext = ReadBalancedToken(InputEnumerator, ArgumentTokens);
// Expand the macro tokens into the output buffer
ExpandFunctionMacro(Macro, ArgumentTokens, OutputTokens, bIsConditional, CurrentLine(), IgnoreMacros);
}
// If the macro expanded to nothing, quit
if(OutputTokens.Count <= NumTokens)
{
break;
}
// Make sure the space is propagated to the expanded macro
OutputTokens[NumTokens] = MergeLeadingSpace(OutputTokens[NumTokens], bHasLeadingSpace);
// Mark any tokens matching the macro name as not to be expanded again. This can happen with recursive object macros, eg. #define DWORD ::DWORD
for(int Idx = NumTokens; Idx < OutputTokens.Count; Idx++)
{
if(OutputTokens[Idx].Type == TokenType.Identifier && OutputTokens[Idx].Text == Macro.Name)
{
OutputTokens[Idx] = new Token(OutputTokens[Idx].Text, TokenType.Identifier, OutputTokens[Idx].Flags | TokenFlags.DisableExpansion);
}
}
}
}
return bMoveNext;
}
/// <summary>
/// Expand an object macro
/// </summary>
/// <param name="Macro">The functional macro</param>
/// <param name="OutputTokens">The list to receive the output tokens</param>
/// <param name="bIsConditional">Whether the macro is being expanded in a conditional context, allowing use of the 'defined' keyword</param>
/// <param name="CurrentLine">The line number being processed</param>
/// <param name="IgnoreMacros">List of macros currently being expanded, which should be ignored for recursion</param>
void ExpandObjectMacro(PreprocessorMacro Macro, List<Token> OutputTokens, bool bIsConditional, int CurrentLine, List<PreprocessorMacro> IgnoreMacros)
{
// Special handling for the __LINE__ directive, since we need an updated line number for the current token
if(Macro.Name == "__FILE__")
{
Token Token = new Token(String.Format("\"{0}\"", CurrentFile.FileName.Replace("\\", "\\\\")), TokenType.StringLiteral, TokenFlags.None);
OutputTokens.Add(Token);
}
else if(Macro.Name == "__LINE__")
{
Token Token = new Token((CurrentLine + 1).ToString(), TokenType.NumericLiteral, TokenFlags.None);
OutputTokens.Add(Token);
}
else if(Macro.Name == "__COUNTER__")
{
Token Token = new Token((Counter++).ToString(), TokenType.NumericLiteral, TokenFlags.None);
OutputTokens.Add(Token);
}
else
{
int OutputTokenCount = OutputTokens.Count;
// Expand all the macros
IgnoreMacros.Add(Macro);
ExpandMacrosRecursively(Macro.Tokens, OutputTokens, bIsConditional, CurrentLine, IgnoreMacros);
IgnoreMacros.RemoveAt(IgnoreMacros.Count - 1);
// Concatenate any adjacent tokens
for(int Idx = OutputTokenCount + 1; Idx < OutputTokens.Count - 1; Idx++)
{
if(OutputTokens[Idx].Text == "##")
{
OutputTokens[Idx - 1] = Token.Concatenate(OutputTokens[Idx - 1], OutputTokens[Idx + 1]);
OutputTokens.RemoveRange(Idx, 2);
Idx--;
}
}
}
}
/// <summary>
/// Expand a function macro
/// </summary>
/// <param name="Macro">The functional macro</param>
/// <param name="OutputTokens">The list to receive the output tokens</param>
/// <param name="bIsConditional">Whether the macro is being expanded in a conditional context, allowing use of the 'defined' keyword</param>
/// <param name="CurrentLine">The line number being processed</param>
/// <param name="IgnoreMacros">List of macros currently being expanded, which should be ignored for recursion</param>
void ExpandFunctionMacro(PreprocessorMacro Macro, List<Token> ArgumentListTokens, List<Token> OutputTokens, bool bIsConditional, int CurrentLine, List<PreprocessorMacro> IgnoreMacros)
{
// Replace any newlines with spaces, and merge them with the following token
for(int Idx = 0; Idx < ArgumentListTokens.Count; Idx++)
{
if(ArgumentListTokens[Idx].Text == "\n")
{
if(Idx + 1 < ArgumentListTokens.Count)
{
ArgumentListTokens[Idx + 1] = MergeLeadingSpace(ArgumentListTokens[Idx + 1], true);
}
ArgumentListTokens.RemoveAt(Idx--);
}
}
// Split the arguments out into separate lists
List<List<Token>> Arguments = new List<List<Token>>();
if(ArgumentListTokens.Count > 2)
{
for(int Idx = 1;;Idx++)
{
if (!Macro.HasVariableArgumentList || Arguments.Count < Macro.Parameters.Count)
{
Arguments.Add(new List<Token>());
}
List<Token> Argument = Arguments[Arguments.Count - 1];
int InitialIdx = Idx;
while (Idx < ArgumentListTokens.Count - 1 && ArgumentListTokens[Idx].Text != ",")
{
if (!ReadBalancedToken(ArgumentListTokens, ref Idx, Argument))
{
throw new PreprocessorException("Invalid argument");
}
}
if(Argument.Count > 0 && Arguments[Arguments.Count - 1][0].HasLeadingSpace)
{
Argument[0] = new Token(Argument[0].Text, Argument[0].Type, Argument[0].Flags & ~TokenFlags.HasLeadingSpace);
}
bool bHasLeadingSpace = false;
for(int TokenIdx = 0; TokenIdx < Argument.Count; TokenIdx++)
{
if(Argument[TokenIdx].Text.Length == 0)
{
bHasLeadingSpace |= Argument[TokenIdx].HasLeadingSpace;
Argument.RemoveAt(TokenIdx--);
}
else
{
Argument[TokenIdx] = MergeLeadingSpace(Argument[TokenIdx], bHasLeadingSpace);
bHasLeadingSpace = false;
}
}
if (Argument.Count == 0)
{
Argument.Add(new Token("", TokenType.Placemarker, TokenFlags.None));
Argument.Add(new Token("", TokenType.Placemarker, bHasLeadingSpace? TokenFlags.HasLeadingSpace : TokenFlags.None));
}
if(Idx == ArgumentListTokens.Count - 1)
{
break;
}
if (ArgumentListTokens[Idx].Text != ",")
{
throw new PreprocessorException("Expected ',' between arguments");
}
if (Macro.HasVariableArgumentList && Arguments.Count == Macro.Parameters.Count && Idx < ArgumentListTokens.Count - 1)
{
Arguments[Arguments.Count - 1].Add(ArgumentListTokens[Idx]);
}
}
}
// Add an empty variable argument if one was not specified
if (Macro.HasVariableArgumentList && Arguments.Count == Macro.Parameters.Count - 1)
{
Arguments.Add(new List<Token> { new Token("", TokenType.Placemarker, TokenFlags.None) });
}
// Validate the argument list
if (Arguments.Count != Macro.Parameters.Count)
{
throw new PreprocessorException("Incorrect number of arguments to macro");
}
// Expand each one of the arguments
List<List<Token>> ExpandedArguments = new List<List<Token>>();
for (int Idx = 0; Idx < Arguments.Count; Idx++)
{
List<Token> NewArguments = new List<Token>();
ExpandMacrosRecursively(Arguments[Idx], NewArguments, bIsConditional, CurrentLine, IgnoreMacros);
ExpandedArguments.Add(NewArguments);
}
// Substitute all the argument tokens
List<Token> ExpandedTokens = new List<Token>();
for(int Idx = 0; Idx < Macro.Tokens.Count; Idx++)
{
Token Token = Macro.Tokens[Idx];
if(Token.Text == "#" && Idx + 1 < Macro.Tokens.Count)
{
// Stringizing operator
int ParamIdx = Macro.FindParameterIndex(Macro.Tokens[++Idx].Text);
if (ParamIdx == -1)
{
throw new PreprocessorException("{0} is not an argument name", Macro.Tokens[Idx].Text);
}
ExpandedTokens.Add(new Token(String.Format("\"{0}\"", Token.Format(Arguments[ParamIdx]).Replace("\\", "\\\\").Replace("\"", "\\\"")), TokenType.StringLiteral, Token.Flags & TokenFlags.HasLeadingSpace));
}
else if(Macro.HasVariableArgumentList && Idx + 2 < Macro.Tokens.Count && Token.Text == "," && Macro.Tokens[Idx + 1].Text == "##" && Macro.Tokens[Idx + 2].Text == PreprocessorMacro.VariadicParameter)
{
// Special MSVC/GCC extension: ', ## __VA_ARGS__' removes the comma if __VA_ARGS__ is empty. MSVC seems to format the result with a forced space.
List<Token> ExpandedArgument = ExpandedArguments[ExpandedArguments.Count - 1];
if(ExpandedArgument.Any(x => x.Text.Length > 0))
{
ExpandedTokens.Add(Token);
AppendTokensWithWhitespace(ExpandedTokens, ExpandedArgument, false);
Idx += 2;
}
else
{
ExpandedTokens.Add(new Token("", TokenType.Placemarker, Token.Flags & TokenFlags.HasLeadingSpace));
ExpandedTokens.Add(new Token("", TokenType.Placemarker, TokenFlags.HasLeadingSpace));
Idx += 2;
}
}
else if (Token.Type == TokenType.Identifier)
{
// Expand a parameter
int ParamIdx = Macro.FindParameterIndex(Token.Text);
if(ParamIdx == -1)
{
ExpandedTokens.Add(Token);
}
else if(Idx > 0 && Macro.Tokens[Idx - 1].Text == "##")
{
AppendTokensWithWhitespace(ExpandedTokens, Arguments[ParamIdx], Token.HasLeadingSpace);
}
else
{
AppendTokensWithWhitespace(ExpandedTokens, ExpandedArguments[ParamIdx], Token.HasLeadingSpace);
}
}
else
{
ExpandedTokens.Add(Token);
}
}
// Concatenate adjacent tokens
for (int Idx = 1; Idx < ExpandedTokens.Count - 1; Idx++)
{
if(ExpandedTokens[Idx].Text == "##")
{
Token ConcatenatedToken = Token.Concatenate(ExpandedTokens[Idx - 1], ExpandedTokens[Idx + 1]);
ExpandedTokens.RemoveRange(Idx, 2);
ExpandedTokens[--Idx] = ConcatenatedToken;
}
}
// Finally, return the expansion of this
IgnoreMacros.Add(Macro);
ExpandMacrosRecursively(ExpandedTokens, OutputTokens, bIsConditional, CurrentLine, IgnoreMacros);
IgnoreMacros.RemoveAt(IgnoreMacros.Count - 1);
}
/// <summary>
/// Appends a list of tokens to another list, setting the leading whitespace flag to the given value
/// </summary>
/// <param name="OutputTokens">List to receive the appended tokens</param>
/// <param name="InputTokens">List of tokens to append</param>
/// <param name="bHasLeadingSpace">Whether there is space before the first token</param>
void AppendTokensWithWhitespace(List<Token> OutputTokens, List<Token> InputTokens, bool bHasLeadingSpace)
{
if(InputTokens.Count > 0)
{
OutputTokens.Add(MergeLeadingSpace(InputTokens[0], bHasLeadingSpace));
OutputTokens.AddRange(InputTokens.Skip(1));
}
}
/// <summary>
/// Copies a single token from one list of tokens to another, or if it's an opening parenthesis, the entire subexpression until the closing parenthesis.
/// </summary>
/// <param name="InputTokens">The input token list</param>
/// <param name="InputIdx">First token index in the input token list. Set to the last uncopied token index on return.</param>
/// <param name="OutputTokens">List to recieve the output tokens</param>
/// <returns>True if a balanced expression was read, or false if the end of the list was encountered before finding a matching token</returns>
bool ReadBalancedToken(List<Token> InputTokens, ref int InputIdx, List<Token> OutputTokens)
{
// Copy a single token to the output list
Token Token = InputTokens[InputIdx++];
OutputTokens.Add(Token);
// If it was the start of a subexpression, copy until the closing parenthesis
if (Token.Type == TokenType.LeftParen)
{
// Copy the contents of the subexpression
for (;;)
{
if (InputIdx == InputTokens.Count)
{
return false;
}
if (InputTokens[InputIdx].Type == TokenType.RightParen)
{
break;
}
if (!ReadBalancedToken(InputTokens, ref InputIdx, OutputTokens))
{
return false;
}
}
// Copy the closing parenthesis
Token = InputTokens[InputIdx++];
OutputTokens.Add(Token);
}
return true;
}
/// <summary>
/// Copies a single token from one list of tokens to another, or if it's an opening parenthesis, the entire subexpression until the closing parenthesis.
/// </summary>
/// <param name="InputTokens">The input token list</param>
/// <param name="InputIdx">First token index in the input token list. Set to the last uncopied token index on return.</param>
/// <param name="OutputTokens">List to recieve the output tokens</param>
/// <returns>True if a balanced expression was read, or false if the end of the list was encountered before finding a matching token</returns>
bool ReadBalancedToken(IEnumerator<Token> InputEnumerator, List<Token> OutputTokens)
{
// Copy a single token to the output list
Token Token = InputEnumerator.Current;
bool bMoveNext = InputEnumerator.MoveNext();
OutputTokens.Add(Token);
// If it was the start of a subexpression, copy until the closing parenthesis
if (Token.Type == TokenType.LeftParen)
{
// Copy the contents of the subexpression
for (;;)
{
if (!bMoveNext)
{
throw new PreprocessorException("Unbalanced token sequence");
}
if (InputEnumerator.Current.Type == TokenType.RightParen)
{
OutputTokens.Add(InputEnumerator.Current);
bMoveNext = InputEnumerator.MoveNext();
break;
}
bMoveNext = ReadBalancedToken(InputEnumerator, OutputTokens);
}
}
return bMoveNext;
}
/// <summary>
/// Create a file which represents the compiler definitions
/// </summary>
/// <param name="PreludeFile"></param>
/// <param name="CompileEnvironment"></param>
/// <param name="WorkingDir"></param>
/// <param name="Definitions"></param>
public static void CreatePreludeFile(FileReference PreludeFile, CompileEnvironment CompileEnvironment, DirectoryReference WorkingDir)
{
// Spawn the compiler and write the prelude file
string CompilerName = CompileEnvironment.Compiler.GetFileName().ToLowerInvariant();
if(CompileEnvironment.CompilerType == CompilerType.Clang)
{
// Create a dummy input file
FileReference InputFile = PreludeFile.ChangeExtension(".in.cpp");
File.WriteAllText(InputFile.FullName, "");
// Spawn clang and parse the output
using (StreamWriter Writer = new StreamWriter(PreludeFile.FullName))
{
Utility.Run(CompileEnvironment.Compiler, String.Join(" ", CompileEnvironment.Options.Where(x => x.Name == "-target").Select(x => x.ToString())) + " -dM -E -x c++ " + InputFile.FullName, WorkingDir, new LineBasedTextWriterWrapper(Writer));
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
}
}
else if(CompileEnvironment.CompilerType == CompilerType.VisualC)
{
// Create a query file
FileReference InputFile = PreludeFile.ChangeExtension(".in");
using (StreamWriter InputWriter = new StreamWriter(InputFile.FullName))
{
InputWriter.WriteLine("#define STRINGIZE_2(x) #x");
InputWriter.WriteLine("#define STRINGIZE(x) STRINGIZE_2(x)");
string[] MacroNames = Properties.Resources.Prelude.Split('\n').Select(x => x.Trim()).Where(x => x.Length > 0 && !x.StartsWith(";")).ToArray();
foreach (string MacroName in MacroNames)
{
InputWriter.WriteLine("");
InputWriter.WriteLine("#ifdef {0}", MacroName);
InputWriter.WriteLine("#pragma message(\"#define {0} \" STRINGIZE({0}))", MacroName);
InputWriter.WriteLine("#endif");
}
}
// Invoke the compiler and capture the output into a string
StringWriter OutputWriter = new StringWriter();
string FullCommandLine = String.Format("{0} /Zs /TP {1}", CompileEnvironment.GetCommandLine(), InputFile.FullName);
Utility.Run(CompileEnvironment.Compiler, FullCommandLine, WorkingDir, new LineBasedTextWriterWrapper(OutputWriter));
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
// Filter the output
using (StreamWriter Writer = new StreamWriter(PreludeFile.FullName))
{
foreach(string Line in OutputWriter.ToString().Split('\n').Select(x => x.Trim()).Where(x => x.Length > 0))
{
if(Line.StartsWith("#define"))
{
Writer.WriteLine(Line);
}
else if(Line.Trim() != InputFile.GetFileName() && !Line.StartsWith("time("))
{
throw new Exception(String.Format("Unexpected output from compiler: {0}", OutputWriter.ToString()));
}
}
}
}
else
{
throw new NotImplementedException("Unknown compiler: " + CompilerName);
}
}
/// <summary>
/// Preprocess the current file and write it to the given output
/// </summary>
/// <param name="InputFile">The file to read from</param>
/// <param name="OutputFile">File to output to</param>
public void PreprocessFile(string InputFileName, FileReference OutputFile, IEnumerable<FileReference> ForceIncludeFiles)
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
{
using (StreamWriter Writer = new StreamWriter(OutputFile.FullName))
{
List<FileReference> InputFiles = new List<FileReference>();
InputFiles.AddRange(ForceIncludeFiles);
InputFiles.Add(new FileReference(InputFileName));
foreach(FileReference InputFile in InputFiles)
{
WorkspaceFile InputWorkspaceFile = Workspace.GetFile(InputFile);
PreprocessFile(new PreprocessorFile(InputFile.FullName, InputWorkspaceFile), Writer);
}
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
}
}
/// <summary>
/// Preprocess the current file to the given output stream
/// </summary>
/// <param name="InputFile">The file to read from</param>
/// <param name="Writer">Stream to output to</param>
bool PreprocessFile(PreprocessorFile InputFile, TextWriter Writer)
{
bool bResult = false;
if(PushFile(InputFile))
{
FileReference InputFileLocation = InputFile.Location;
SourceFile SourceFile = InputFile.WorkspaceFile.ReadSourceFile();
// Always write a #line directive at the start of the file
Writer.WriteLine(FormatLineDirective(1));
// Write the rest of the file
TextLocation Location = TextLocation.Origin;
foreach (PreprocessorMarkup Markup in SourceFile.Markup)
{
// Write everything since the last item
if (Markup.Location > Location)
{
if (IsBranchActive())
{
WriteText(SourceFile.Text, Location, Markup.Location, Writer);
}
else
{
WriteBlankedText(SourceFile.Text, Location, Markup.Location, Writer);
}
Location = Markup.Location;
}
// Check the type of this markup
if (Markup.Type == PreprocessorMarkupType.Text)
{
// Write the expanded macro tokens if the current block is active, or blank lines if not
if (IsBranchActive())
{
WriteExpandedText(SourceFile.Text, Location, Markup.EndLocation, Writer);
}
else
{
WriteBlankedText(SourceFile.Text, Location, Markup.EndLocation, Writer);
}
Location = Markup.EndLocation;
}
else
{
// Capture initial state that we need later on
bool bActive = IsBranchActive();
// Check whether we want a #line directive after this point
bool bOutputLineDirective = false;
if(Markup.Type == PreprocessorMarkupType.Endif)
{
if(Branches.Peek().HasFlag(BranchState.HasIfDirective) && Branches.Peek().HasFlag(BranchState.ParentIsActive))
{
bOutputLineDirective = true;
}
if(Branches.Peek().HasFlag(BranchState.HasIfdefDirective) && Branches.Peek().HasFlag(BranchState.ParentIsActive) && Branches.Peek().HasFlag(BranchState.Taken))
{
bOutputLineDirective = true;
}
if(Branches.Peek().HasFlag(BranchState.HasIfndefDirective) && Branches.Peek().HasFlag(BranchState.ParentIsActive) && Branches.Peek().HasFlag(BranchState.Taken))
{
bOutputLineDirective = true;
}
}
// Parse the markup
if (Markup.Type == PreprocessorMarkupType.Include)
{
bOutputLineDirective |= WriteInclude(Markup.Tokens, Markup.Location.LineIdx, Writer);
}
else
{
ParseMarkup(Markup.Type, Markup.Tokens, Markup.Location.LineIdx);
}
// Find the newline token (note - may be skipping over escaped newlines here)
Location = Markup.EndLocation;
// Write the contents of this markup, if necessary
if (IsBranchActive())
{
if(Markup.Type == PreprocessorMarkupType.Else)
{
if(Branches.Peek().HasFlag(BranchState.HasIfDirective) || Branches.Peek().HasFlag(BranchState.HasElifDirective))
{
bOutputLineDirective = true;
}
}
else if (Markup.Type == PreprocessorMarkupType.Elif)
{
if(!Branches.Peek().HasFlag(BranchState.HasIfdefDirective))
{
bOutputLineDirective = true;
}
}
}
else
{
if(Markup.Type == PreprocessorMarkupType.Elif)
{
if(!Branches.Peek().HasFlag(BranchState.Taken) && Branches.Peek().HasFlag(BranchState.ParentIsActive) && !Branches.Peek().HasFlag(BranchState.HasIfdefDirective))
{
bOutputLineDirective = true;
}
}
if(Markup.Type == PreprocessorMarkupType.Else)
{
if(!Branches.Peek().HasFlag(BranchState.Taken) && Branches.Peek().HasFlag(BranchState.ParentIsActive))// && !Branches.Peek().HasFlag(BranchState.InitialIfCondition))
{
bOutputLineDirective = true;
}
}
}
// Write any text or escaped newlines for this markup
if (bActive && Markup.Type == PreprocessorMarkupType.Pragma && (Markup.Tokens[0].Text != "pop_macro" && Markup.Tokens[0].Text != "push_macro"))
{
List<Token> ExpandedTokens = new List<Token>();
ExpandMacros(Markup.Tokens, ExpandedTokens, false, Markup.Location.LineIdx);
Writer.Write("#pragma {0}", Token.Format(ExpandedTokens));
}
else if(!bActive || Markup.Type == PreprocessorMarkupType.Define)
{
for (int EscapedLineIdx = Markup.Location.LineIdx; EscapedLineIdx < Markup.EndLocation.LineIdx - 1; EscapedLineIdx++)
{
Writer.WriteLine();
}
}
// Output the line directive
if(bOutputLineDirective)
{
Writer.WriteLine(FormatLineDirective(Markup.EndLocation.LineIdx + 1));
}
else
{
Writer.WriteLine();
}
}
}
WriteText(SourceFile.Text, Location, SourceFile.Text.End, Writer);
// Automatically treat header guards as #pragma once style directives
if (SourceFile.HasHeaderGuard)
{
PragmaOnceFiles.Add(SourceFile.Location);
}
// Pop the file from the include stack
PopFile();
bResult = true;
}
return bResult;
}
/// <summary>
/// Resolve an included file and write it to an output stream
/// </summary>
/// <param name="Tokens">Tokens for the include directive</param>
/// <param name="CurrentLine">The current line</param>
/// <param name="Writer">Writer for the output text</param>
/// <returns>True if an include was written, false if the current branch is not active, or the file has already been included</returns>
bool WriteInclude(List<Token> Tokens, int CurrentLine, TextWriter Writer)
{
bool bResult = false;
if (IsBranchActive())
{
PreprocessorFile IncludeFile = ResolveInclude(Tokens, CurrentLine);
bResult = PreprocessFile(IncludeFile, Writer);
}
return bResult;
}
/// <summary>
/// Preprocess the current file to the given output stream
/// </summary>
/// <param name="Text">The text buffer for the current file</param>
/// <param name="Location">Start of the region to copy to the output stream</param>
/// <param name="EndLocation">End of the region to copy to the output stream</param>
/// <param name="Writer">Writer for the output text</param>
void WriteText(TextBuffer Text, TextLocation Location, TextLocation EndLocation, TextWriter Writer)
{
// Write the whitespace
while(EndLocation.LineIdx > Location.LineIdx)
{
if(Text.Lines[Location.LineIdx].EndsWith("*\\"))
{
// Hacky fix to make preprocessed output the same as MSVC. When writing a multiline comment ending with *\ (as appears in copyright boilerplate for
// headers like winver.h), both characters are removed and the * is pushed into the start of the next line.
Writer.WriteLine(Text.Lines[Location.LineIdx].Substring(Location.ColumnIdx, Text.Lines[Location.LineIdx].Length - 2));
Writer.Write("*");
}
else
{
Writer.WriteLine(Text.Lines[Location.LineIdx].Substring(Location.ColumnIdx));
}
Location.ColumnIdx = 0;
Location.LineIdx++;
}
// Write the last line
if(Location.ColumnIdx < EndLocation.ColumnIdx)
{
Writer.Write(Text.Lines[Location.LineIdx].Substring(Location.ColumnIdx, EndLocation.ColumnIdx - Location.ColumnIdx));
}
}
/// <summary>
/// Preprocess the current file to the given output stream
/// </summary>
/// <param name="Text">The text buffer for the current file</param>
/// <param name="Location">Start of the region to copy to the output stream</param>
/// <param name="EndLocation">End of the region to copy to the output stream</param>
/// <param name="Writer">Writer for the output text</param>
void WriteBlankedText(TextBuffer Text, TextLocation Location, TextLocation EndLocation, TextWriter Writer)
{
// Write the whitespace
while(EndLocation.LineIdx > Location.LineIdx)
{
Writer.WriteLine();
Location.ColumnIdx = 0;
Location.LineIdx++;
}
// Write the last line
if(Location.ColumnIdx < EndLocation.ColumnIdx)
{
//Writer.Write(Text.Lines[Location.LineIdx].Substring(Location.ColumnIdx, EndLocation.ColumnIdx - Location.ColumnIdx));
}
}
/// <summary>
/// Write a portion of the given file to the output stream, expanding macros in it
/// </summary>
/// <param name="Text">The text buffer for the current file</param>
/// <param name="Location">Start of the region to copy to the output stream</param>
/// <param name="EndLocation">End of the region to copy to the output stream</param>
/// <param name="Writer">Writer for the output text</param>
void WriteExpandedText(TextBuffer Text, TextLocation Location, TextLocation EndLocation, TextWriter Writer)
{
int ExpectedLineIdx = Location.LineIdx;
// Expand any macros in this block of tokens
TokenReader Reader = new TokenReader(Text, Location);
for(bool bMoveNext = Reader.MoveNext(); bMoveNext; )
{
// Write the whitespace to the output stream
WriteText(Text, Reader.TokenWhitespaceLocation, Reader.TokenLocation, Writer);
ExpectedLineIdx += Reader.TokenLocation.LineIdx - Reader.TokenWhitespaceLocation.LineIdx;
// Check if we've reached the end
if(Reader.TokenLocation >= EndLocation)
{
break;
}
// Write out the current token
if(Reader.Current.Text == "\n")
{
Writer.WriteLine();
if(++ExpectedLineIdx != Reader.TokenEndLocation.LineIdx)
{
ExpectedLineIdx = Reader.TokenEndLocation.LineIdx;
Writer.WriteLine(FormatLineDirective(ExpectedLineIdx + 1));
}
bMoveNext = Reader.MoveNext();
}
else
{
// MSVC includes comments in any macro arguments before any expanded tokens. Save the potential first argument location in case this happens.
TextLocation FirstArgumentLocation = Reader.TokenEndLocation;
// Expand all the tokens
List<Token> ExpandedTokens = new List<Token>();
bMoveNext = ReadExpandedToken(Reader, ExpandedTokens, false, () => Reader.TokenLocation.LineIdx, new List<PreprocessorMacro>());
// If we expanded a macro, insert all the comment tokens
if(Reader.TokenWhitespaceLocation > FirstArgumentLocation)
{
TokenReader CommentReader = new TokenReader(Text, FirstArgumentLocation);
while(CommentReader.MoveNext() && CommentReader.TokenLocation < Reader.TokenWhitespaceLocation)
{
string[] Comments = Text.Extract(CommentReader.TokenWhitespaceLocation, CommentReader.TokenLocation).Select(x => x.EndsWith("\\")? x.Substring(0, x.Length - 1) : x).ToArray();
Comments[0] = Comments[0].TrimStart();
for(int Idx = 0; Idx < Comments.Length - 1; Idx++)
{
Writer.WriteLine(Comments[Idx]);
}
Writer.Write(Comments[Comments.Length - 1].TrimEnd());
}
}
// Now write the expanded tokens
Writer.Write(Token.Format(ExpandedTokens));
}
}
}
}
/// <summary>
/// Tests for the preprocessor
/// </summary>
static class PreprocessorTests
{
public static void Run(bool bTestFailureCases)
{
RunTest("token", "token\n");
RunTest("#", "");
RunTest("#define X token\nX", "token\n");
RunTest("#define X(x) token x other\nX(and one).", "token and one other.\n");
RunTest("#define X(x,y) token x and y\nX(1, 2).", "token 1 and 2.\n");
RunTest("#define INC(x) (x + 2)\nINC", "INC\n");
RunTest("#define TEST(x) x\\\n?\nTEST(A)", "A?\n");
//RunTest("%:define X token\nX", "<token>", Preprocess);
RunTest("#define A B C D\nA", "B C D\n");
RunTest("#define A B ## D\nA", "BD\n");
RunTest("#define F(A) <A>\nF(x)", "<x>\n");
RunTest("#define F(A,B) <A,B>\nF(x,y) + 1", "<x,y> + 1\n");
RunTest("#define F(A,B,C) <A,B,C>\nF(x,y,z)", "<x,y,z>\n");
RunTest("#define F(...) <__VA_ARGS__>\nF(x)", "<x>\n");
RunTest("#define F(...) <__VA_ARGS__>\nF(x,y)", "<x,y>\n");
RunTest("#define F(A,...) <A,__VA_ARGS__>\nF(x,y)", "<x,y>\n");
RunTest("#define F(A,...) <A, __VA_ARGS__>\nF(x,y)", "<x, y>\n");
RunTest("#define F(A,...) <A, __VA_ARGS__>\nF(x,y, z)", "<x, y, z>\n");
RunTest("#define X list of tokens\nX", "list of tokens\n");
RunTest("#define LST list\n#define TOKS tokens\n#define LOTS LST of TOKS\nLOTS LOTS", "list of tokens list of tokens\n");
RunTest("#define LOTS LST of TOKS\n#define LST list\n#define TOKS tokens\nLOTS LOTS", "list of tokens list of tokens\n");
RunTest("#define FUNC(x) arg=x.\nFUNC(var) FUNC(2)", "arg=var. arg=2.\n");
RunTest("#define FUNC(x,y,z) int n = z+y*x;\nFUNC(1,2,3)", "int n = 3+2*1;\n");
RunTest("#define X 20\n#define FUNC(x,y) x+y\nx=FUNC(X,Y);", "x=20+Y;\n");
RunTest("#define FA(x,y) FB(x,y)\n#define FB(x,y) x + y\nFB(1,2);", "1 + 2;\n");
RunTest("#define PRINTF(...) printf(__VA_ARGS__)\nPRINTF()", "printf()\n");
RunTest("#define PRINTF(...) printf(__VA_ARGS__)\nPRINTF(\"hello\")", "printf(\"hello\")\n");
RunTest("#define PRINTF(...) printf(__VA_ARGS__)\nPRINTF(\"%d\", 1)", "printf(\"%d\", 1)\n");
RunTest("#define PRINTF(FORMAT, ...) printf(FORMAT, __VA_ARGS__)\nPRINTF(\"test\")", "printf(\"test\", )\n");
RunTest("#define PRINTF(FORMAT, ...) printf(FORMAT, __VA_ARGS__)\nPRINTF(\"test %s\", \"hello\")", "printf(\"test %s\", \"hello\")\n");
RunTest("#define PRINTF(FORMAT, ...) printf(FORMAT, __VA_ARGS__)\nPRINTF(\"test %s %d\", \"hello\", 1)", "printf(\"test %s %d\", \"hello\", 1)\n");
RunTest("#define PRINTF(FORMAT, ...) printf(FORMAT, ## __VA_ARGS__)\nPRINTF(\"test\")", "printf(\"test\" )\n");
RunTest("#define INC(x) (x + 2)\nINC(\n1\n)", "(1 + 2)\n");
RunTest("#define STRINGIZE(ARG) #ARG\nSTRINGIZE(+=)", "\"+=\"\n");
RunTest("#define STRINGIZE(ARG) #ARG\nSTRINGIZE(:>)", "\":>\"\n");
RunTest("#define STRINGIZE(ARG) #ARG\nSTRINGIZE(3.1415)", "\"3.1415\"\n");
RunTest("#define CONCAT(X, Y) X ## Y\nCONCAT(+, =)", "+=\n");
RunTest("#define CONCAT(X, Y) X ## Y\nCONCAT(3, .14159)", "3.14159\n");
RunTest("#define CONCAT(X, Y) X ## Y\nCONCAT(3, hello)", "3hello\n");
RunTest("#define CONCAT(X, Y) X ## #Y\nCONCAT(u, hello)", "u\"hello\"\n");
RunTest("#define CONCAT(X, ...) X ## __VA_ARGS__\nCONCAT(hello) there", "hello there\n");
RunTest("#define CONCAT(X, ...) X ## __VA_ARGS__\nCONCAT(hello, there)", "hellothere\n");
RunTest("#if 1\nfirst_branch\n#endif\nend", "first_branch\nend\n");
RunTest("#if 0\nfirst_branch\n#endif\nend", "end\n");
RunTest("#if 1\nbranch_1\n#else\nbranch_2\n#endif", "branch_1\n");
RunTest("#if 0\nbranch_1\n#else\nbranch_2\n#endif", "branch_2\n");
RunTest("#define A\n#ifdef A\nYes\n#endif", "Yes\n");
RunTest("#define B\n#ifdef A\nYes\n#endif\nNo", "No\n");
RunTest("#define A\n#ifndef A\nYes\n#endif\nNo", "No\n");
RunTest("#define B\n#ifndef A\nYes\n#endif", "Yes\n");
RunTest("#define A\n#undef A\n#ifdef A\nYes\n#endif\nNo", "No\n");
RunTest("#define A\n#undef A\n#ifndef A\nYes\n#endif", "Yes\n");
RunTest("#define A\n#ifdef A\nYes\n#else\nNo\n#endif", "Yes\n");
RunTest("#define B\n#ifdef A\nYes\n#else\nNo\n#endif", "No\n");
RunTest("#define A MACRO\nA", "MACRO\n");
RunTest("#define A MACRO\n#undef A\nA", "A\n");
RunTest("#define A(X) MACRO X\nA(x)", "MACRO x\n");
RunTest("#define A(X) MACRO X\n#undef A\nA(x)", "A(x)\n");
RunTest("#if 1 + 2 > 3\nYes\n#endif\nNo", "No\n");
RunTest("#if 1 + 2 >= 3\nYes\n#endif", "Yes\n");
RunTest("#define ONE 1\n#define TWO 2\n#define PLUS(x, y) x + y\n#if PLUS(ONE, TWO) > 3\nYes\n#endif\nNo", "No\n");
RunTest("#define ONE 1\n#define TWO 2\n#define PLUS(x, y) x + y\n#if PLUS(ONE, TWO) >= 3\nYes\n#endif", "Yes\n");
RunTest("#define ONE 1\n#if defined ONE\nOne\n#elif defined TWO\nTwo\n#else\nThree\n#endif", "One\n");
RunTest("#define TWO 1\n#if defined ONE\nOne\n#elif defined TWO\nTwo\n#else\nThree\n#endif", "Two\n");
RunTest("#define ONE 0\n#if defined(ONE) + defined(TWO) >= 1\nYes\n#else\nNo\n#endif", "Yes\n");
RunTest("#define ONE 0\n#if defined(ONE) + defined(TWO) >= 2\nYes\n#else\nNo\n#endif", "No\n");
RunTest("#define ONE 0\n#define TWO\n#if defined(ONE) + defined(TWO) >= 2\nYes\n#else\nNo\n#endif", "Yes\n");
RunTest("#define REGISTER_NAME(num,name) NAME_##name = num,\nREGISTER_NAME(201, TRUE)", "NAME_TRUE = 201,\n");
RunTest("#define FUNC_2(X) VALUE=X\n#define FUNC_N(X) FUNC_##X\n#define OBJECT FUNC_N(2)\nOBJECT(1234)", "VALUE=1234\n");
RunTest("#define GCC_EXTENSION(...) 123, ## __VA_ARGS__\nGCC_EXTENSION(456)", "123,456\n");
RunTest("#define FUNC(fmt,...) (fmt, ## __VA_ARGS__)\nFUNC(a)", "(a )\n");
RunTest("#define FUNC(fmt,...) (fmt, ## __VA_ARGS__)\nFUNC(a,b)", "(a,b)\n");
RunTest("#define FUNC(fmt,...) (fmt, ## __VA_ARGS__)\nFUNC(a,b )", "(a,b)\n");
RunTest("#define FUNC(fmt, ...) (fmt, ## __VA_ARGS__)\nFUNC(a)", "(a )\n");
RunTest("#define FUNC(fmt, ...) (fmt, ## __VA_ARGS__)\nFUNC(a,b)", "(a,b)\n");
RunTest("#define FUNC(fmt, ...) (fmt, ## __VA_ARGS__)\nFUNC(a,b )", "(a,b)\n");
RunTest("#define EMPTY_TOKEN\n#define FUNC(_FuncName, _HType1, _HArg1) _FuncName(_HType1 _HArg1);\nFUNC(hello, EMPTY_TOKEN, int)", "hello( int);\n");
RunTest("#define EMPTY_TOKEN\n#define GCC_EXTENSION(...) 123 , ## __VA_ARGS__\nGCC_EXTENSION(EMPTY_TOKEN)", "123 \n");
RunTest("#define FUNC(x) Value = x\n#define PP_JOIN(x, y) x ## y\n#define RESULT(x) PP_JOIN(FU, NC)(x)\nRESULT(1234)", "Value = 1234\n");
RunTest("#define VALUE 1234\n#define PP_JOIN(x, y) x ## y\n#define RESULT PP_JOIN(V, ALUE)\nRESULT", "1234\n");
RunTest("#define EMPTY_TOKEN\n#define FUNC(x) (x)\nFUNC(EMPTY_TOKEN A)", "( A)\n");
RunTest("#define EMPTY_TOKEN\n#define FUNC(x,y) (x y)\nFUNC(EMPTY_TOKEN,A)", "( A)\n");
// RunTest("#define EMPTY_TOKEN\n#define FUNC(x) (x)\n#define FUNC_2(x,y) FUNC(x y)\nFUNC_2(EMPTY_TOKEN,A)", "( A)\n");
// RunTest("#define EMPTY_TOKEN\n#define FUNC(x) (x EMPTY_TOKEN)\nFUNC(A)", "(A )\n");
// RunTest("#define EMPTY_TOKEN\n#define FUNC(x,y) (x y)\nFUNC(A,)", "(A)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\nFUNC(x y)", "(x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\nFUNC(x EMPTY y)", "(x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\nFUNC(x EMPTY y EMPTY)", "(x y )\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\nFUNC(EMPTY x EMPTY y)", "( x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\nFUNC( EMPTY x EMPTY y)", "( x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\nFUNC(EMPTY x EMPTY y )", "( x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\n#define FUNC_2(x) FUNC(x)\nFUNC_2(x y)", "(x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\n#define FUNC_2(x) FUNC(x)\nFUNC_2(x EMPTY y)", "(x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\n#define FUNC_2(x) FUNC(x)\nFUNC_2(x EMPTY y EMPTY)", "(x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\n#define FUNC_2(x) FUNC(x)\nFUNC_2(EMPTY x EMPTY y)", "( x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\n#define FUNC_2(x) FUNC(x)\nFUNC_2( EMPTY x EMPTY y)", "( x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) (x)\n#define FUNC_2(x) FUNC(x)\nFUNC_2(EMPTY x EMPTY y )", "( x y)\n");
RunTest("#define EMPTY\n#define FUNC(x) ( x )\nFUNC(EMPTY EMPTY)", "( )\n");
RunTest("#define EMPTY\n#define FUNC(x) ( x)\nFUNC(EMPTY EMPTY x)", "( x)\n");
RunTest("#define EMPTY\n#define FUNC(x,y) ( x y)\n#define FUNC2(x,y) FUNC(x,y)\nFUNC2(EMPTY EMPTY EMPTY, x)", "( x)\n");
RunTest("#define EMPTY\n#define DOUBLE_EMPTY EMPTY EMPTY\n#define FUNC(x) ( x)\nFUNC(DOUBLE_EMPTY x)", "( x)\n");
RunTest("#define EMPTY\n#define DOUBLE_EMPTY EMPTY EMPTY\n#define FUNC(x,y) ( x y )\n#define FUNC2(x,y) FUNC(x,y)\nFUNC2(DOUBLE_EMPTY EMPTY, x)", "( x )\n");
RunTest("#define EMPTY\n#define FUNC(x,y) ( x y )\nFUNC(EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY, x)", "( x )\n");
RunTest("#define _NON_MEMBER_CALL(FUNC, CV_REF_OPT) FUNC(X,CV_REF_OPT)\n#define _NON_MEMBER_CALL_CV(FUNC, REF_OPT) _NON_MEMBER_CALL(FUNC, REF_OPT) _NON_MEMBER_CALL(FUNC, const REF_OPT)\n#define _NON_MEMBER_CALL_CV_REF(FUNC) _NON_MEMBER_CALL_CV(FUNC, ) _NON_MEMBER_CALL_CV(FUNC, &)\n#define _IS_FUNCTION(X,CV_REF_OPT) (CV_REF_OPT)\n_NON_MEMBER_CALL_CV_REF(_IS_FUNCTION)", "() (const) (&) (const &)\n");
RunTest("#define TEXT(x) L ## x\n#define checkf(expr, format, ...) FDebug::AssertFailed(#expr, format, ##__VA_ARGS__)\ncheckf( true, TEXT( \"hello world\" ) );", "FDebug::AssertFailed(\"true\", L\"hello world\" );\n");
if (bTestFailureCases)
{
RunTest("#define", null);
RunTest("#define INC(x) (x + 2)\nINC(", null);
RunTest("#define +", null);
RunTest("#define A B __VA_ARGS__ D\nA", null);
RunTest("#define A ## B\nA", null);
RunTest("#define A B ##\nA", null);
RunTest("#define defined not_defined", null);
RunTest("#define F(A) <A>\nF(x,y) + 1", null);
RunTest("#define F(A,) <A>\nF(x)", null);
RunTest("#define F(A+) <A>\nF(x)", null);
RunTest("#define F(A,A) <A>\nF(x,y)", null);
RunTest("#define F(A,B) <A,B>\nF(x) + 1", null);
RunTest("#define F(A,B,) <A,B>\nF(x,y) + 1", null);
RunTest("#define F(...) <__VA_ARGS__>\nF(x,)", null);
RunTest("#define F(A...) <A, __VA_ARGS__>\nF(x)", null);
RunTest("#define F(A...) <A, __VA_ARGS__>\nF(x,y)", null);
RunTest("#define F(A,__VA_ARGS__) <A, __VA_ARGS__>\nF(x,y)", null);
RunTest("#define F(A) #+\nF(x)", null);
RunTest("#define F(A) #B\nF(x)", null);
RunTest("#define F(A) ## A\nF(x)", null);
RunTest("#define F(A) A ##\nF(x)", null);
RunTest("#define F(A) <__VA_ARGS__>\nF(x)", null);
RunTest("#define INC(x\n)", null);
RunTest("#if 1\nbranch_1\n#else garbage\nbranch_2\n#endif\nend", null);
RunTest("#if 1\nbranch_1\n#else\nbranch_2\n#endif garbage\nend", null);
RunTest("#if 1\nbranch_1\n#else\nbranch_2\n#elif 1\nbranch_3\n#endif\nend", null);
RunTest("#if 1\nbranch_1\n#else\nbranch_2\n#else\nbranch_3\n#endif", null);
RunTest("#if 0\nbranch_1\n#else\nbranch_2\n#else\nbranch_3\n#endif", null);
RunTest("#elif\nbranch_1\n#else\nbranch_2\n#endif\nend", null);
RunTest("#ifdef +\nbranch_1\n#else\nbranch_2\n#endif", null);
RunTest("#ifdef A 1\nbranch_1\n#else\nbranch_2\n#endif", null);
RunTest("#define A()\n#if A(\n)\nOK\n#endif", null);
RunTest("#define A MACRO\n#undef A B\nA", null);
}
}
/// <summary>
/// Preprocess a fragment of code, and check it results in an expected sequence of tokens
/// </summary>
/// <param name="Fragment">The code fragment to preprocess</param>
/// <param name="ExpectedResult">The expected sequence of tokens, as a string. Null to indicate that the input is invalid, and an exception is expected.</param>
static void RunTest(string Fragment, string ExpectedResult)
{
SourceFile File = new SourceFile(null, TextBuffer.FromString(Fragment), SourceFileFlags.Inline);
Preprocessor Instance = new Preprocessor(null, new string[] { }, new DirectoryReference[] { });
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
string Result;
try
{
List<Token> OutputTokens = new List<Token>();
foreach(PreprocessorMarkup Markup in File.Markup)
{
if(Markup.Type == PreprocessorMarkupType.Text)
{
if(Instance.IsBranchActive())
{
List<Token> Tokens = new List<Token>();
TokenReader Reader = new TokenReader(File.Text, Markup.Location);
while(Reader.MoveNext() && Reader.TokenLocation < Markup.EndLocation)
{
Tokens.Add(Reader.Current);
}
Instance.ExpandMacros(Tokens, OutputTokens, false, 0);
}
}
else
{
Instance.ParseMarkup(Markup.Type, Markup.Tokens, 0);
}
}
Result = Token.Format(OutputTokens);
}
catch (PreprocessorException)
{
Result = null;
}
if((ExpectedResult == null && Result != null) || (ExpectedResult != null && Result == null) || (ExpectedResult != null && Result != null && ExpectedResult != Result))
{
string Prefix = "Failed test for: ";
foreach(string Line in Fragment.Split('\n'))
{
Console.WriteLine("{0}{1}", Prefix, Line);
Prefix = new string(' ', Prefix.Length);
}
Console.WriteLine("Expected: {0}", (ExpectedResult != null)? ExpectedResult.Replace("\n", "\\n") : "Exception");
Console.WriteLine("Received: {0}", (Result != null)? Result.Replace("\n", "\\n") : "Exception");
Console.WriteLine();
}
}
/// <summary>
/// Preprocess a set of source files
/// </summary>
/// <param name="WorkingDir">The working directory to invoke the compiler from</param>
/// <param name="PreprocessedDir">The output directory for the preprocessed files</param>
/// <param name="FilePairs">List of files and their compile environment</param>
/// <param name="Log">Writer for log output</param>
public static void WritePreprocessedFiles(DirectoryReference WorkingDir, DirectoryReference PreprocessedDir, IEnumerable<KeyValuePair<FileReference, CompileEnvironment>> FilePairs, LineBasedTextWriter Log)
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
{
PreprocessedDir.CreateDirectory();
// Create separate directories for the compiler and include tool
DirectoryReference CompilerDir = DirectoryReference.Combine(PreprocessedDir, "Compiler");
CompilerDir.CreateDirectory();
DirectoryReference CompilerNormalizedDir = DirectoryReference.Combine(PreprocessedDir, "CompilerNormalized");
CompilerNormalizedDir.CreateDirectory();
DirectoryReference ToolDir = DirectoryReference.Combine(PreprocessedDir, "Tool");
ToolDir.CreateDirectory();
// Write out the prelude file, which we can use to scrape the compiler settings
FileReference PreludeFile = FileReference.Combine(PreprocessedDir, "Prelude.cpp");
Preprocessor.CreatePreludeFile(PreludeFile, FilePairs.First().Value, WorkingDir);
// Process all the files
int MaxDegreeOfParallelism = -1;
if(MaxDegreeOfParallelism == 1)
{
foreach (KeyValuePair<FileReference, CompileEnvironment> Pair in FilePairs)
{
WritePreprocessedFile(Pair.Key, Pair.Value, PreludeFile, WorkingDir, CompilerDir, CompilerNormalizedDir, ToolDir, Log);
}
}
else
{
ParallelOptions Options = new ParallelOptions() { MaxDegreeOfParallelism = MaxDegreeOfParallelism };
Parallel.ForEach(FilePairs, Options, Pair => WritePreprocessedFile(Pair.Key, Pair.Value, PreludeFile, WorkingDir, CompilerDir, CompilerNormalizedDir, ToolDir, Log));
}
}
/// <summary>
/// Preprocess a single file, with this tool and the default compiler
/// </summary>
/// <param name="InputFile">The source file to preprocess</param>
/// <param name="Environment">The environment to compile with</param>
/// <param name="PreludeFile">The prelude source file to use to scrape the compiler's default definitions</param>
/// <param name="WorkingDir">Working directory for the compiler</param>
/// <param name="CompilerDir">Output directory for files preprocessed by the compiler</param>
/// <param name="CompilerNormalizedDir">Output directory for normalized versions of files preprocessed by the compiler</param>
/// <param name="ToolDir">Output directory for files preprocessed by this tool</param>
/// <param name="FileCache">Cache of file locations to their source file</param>
/// <param name="Log">Output log writer</param>
static void WritePreprocessedFile(FileReference InputFile, CompileEnvironment Environment, FileReference PreludeFile, DirectoryReference WorkingDir, DirectoryReference CompilerDir, DirectoryReference CompilerNormalizedDir, DirectoryReference ToolDir, LineBasedTextWriter Log)
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
{
BufferedTextWriter BufferedLog = new BufferedTextWriter();
// Run the preprocessor on it
Preprocessor ToolPreprocessor = new Preprocessor(PreludeFile, Environment.Definitions, Environment.IncludePaths);
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
FileReference ToolOutputFile = FileReference.Combine(ToolDir, InputFile.GetFileNameWithoutExtension() + ".i");
ToolPreprocessor.PreprocessFile(InputFile.FullName, ToolOutputFile, Environment.ForceIncludeFiles);
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
// Generate the compiler output
FileReference CompilerOutputFile = FileReference.Combine(CompilerDir, InputFile.GetFileNameWithoutExtension() + ".i");
string CommandLine = String.Format("{0} {1} /P /C /utf-8 /Fi{2}", Environment.GetCommandLine(), Utility.QuoteArgument(InputFile.FullName), Utility.QuoteArgument(CompilerOutputFile.FullName));
Utility.Run(Environment.Compiler, CommandLine, WorkingDir, BufferedLog);
// Normalize the compiler output
FileReference NormalizedCompilerOutputFile = FileReference.Combine(CompilerNormalizedDir, InputFile.GetFileNameWithoutExtension() + ".i");
NormalizePreprocessedFile(ToolOutputFile, CompilerOutputFile, NormalizedCompilerOutputFile, BufferedLog);
// Copy everything to the output log
lock (Log)
{
BufferedLog.CopyTo(Log, " ");
}
}
/// <summary>
/// Takes an output file generated by this tool, and removes any formatting differences from a compiler-generated preprocessed file.
/// </summary>
/// <param name="ToolFile">File preprocessed by this tool</param>
/// <param name="CompilerFile">Path to the preprocessed file generated by the compiler</param>
/// <param name="NormalizedCompilerFile">Output file for the normalized compiler file</param>
/// <param name="Log"></param>
static int NormalizePreprocessedFile(FileReference ToolFile, FileReference CompilerFile, FileReference NormalizedCompilerFile, LineBasedTextWriter Log)
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
{
// Read the tool file
string[] ToolLines = File.ReadAllLines(ToolFile.FullName);
// Read the compiler file
string[] CompilerLines = File.ReadAllLines(CompilerFile.FullName);
// Write the output file
int NumDifferences = 0;
using (StreamWriter Writer = new StreamWriter(NormalizedCompilerFile.FullName))
{
int ToolIdx = 0;
int CompilerIdx = 0;
while (CompilerIdx < CompilerLines.Length && ToolIdx < ToolLines.Length)
{
string ToolLine = ToolLines[ToolIdx];
string CompilerLine = CompilerLines[CompilerIdx];
// Weird inconsistency with MSVC output where it sometimes adds an extra newline at the end of the file, even if one already exists. Skip over these lines in the compiler output.
if(CompilerLine.Length == 0 && ToolLine.Length > 0 && (CompilerIdx + 1 == CompilerLines.Length || ComparePreprocessedLines(CompilerLines[CompilerIdx + 1], ToolLine)))
{
CompilerIdx++;
continue;
}
// If the lines are semantically equivalent, just ignore the whitespace differences and take the tool line
if (ComparePreprocessedLines(CompilerLine, ToolLine))
{
Writer.WriteLine(ToolLine);
ToolIdx++;
CompilerIdx++;
continue;
}
// Output the difference
if(NumDifferences < 5)
{
Log.WriteLine(" Compiler ({0}): {1}", CompilerIdx, CompilerLine);
Log.WriteLine(" Tool ({0}): {1}", ToolIdx, ToolLine);
Log.WriteLine();
NumDifferences++;
}
// Otherwise try to find a matching line
int PrevCompilerIdx = CompilerIdx;
if(!FindMatchingLines(ToolLines, ref ToolIdx, CompilerLines, ref CompilerIdx))
{
ToolIdx++;
CompilerIdx++;
}
while(PrevCompilerIdx < CompilerIdx)
{
Writer.WriteLine(CompilerLines[PrevCompilerIdx]);
PrevCompilerIdx++;
}
}
while(CompilerIdx < CompilerLines.Length && (CompilerIdx < CompilerLines.Length - 1 || CompilerLines[CompilerIdx].Length > 0))
{
Writer.WriteLine(CompilerLines[CompilerIdx]);
CompilerIdx++;
}
}
return NumDifferences;
}
/// <summary>
/// Update indices into two line lists until we find two matching lines
/// </summary>
/// <param name="ToolLines">Array of lines from tool output</param>
/// <param name="ToolIdx">Index into tool lines</param>
/// <param name="CompilerLines">Array of lines from compiler output</param>
/// <param name="CompilerIdx">Index into compiler lines</param>
/// <returns>True if a matching line was found</returns>
static bool FindMatchingLines(string[] ToolLines, ref int ToolIdx, string[] CompilerLines, ref int CompilerIdx)
{
for(int Offset = 1; ToolIdx + Offset < ToolLines.Length || CompilerIdx + Offset < CompilerLines.Length; Offset++)
{
// Try to match the tool line at this offset with a compiler line at any equal or smaller offset
if(ToolIdx + Offset < ToolLines.Length)
{
string ToolLine = ToolLines[ToolIdx + Offset];
for(int OtherCompilerIdx = CompilerIdx; OtherCompilerIdx <= CompilerIdx + Offset && OtherCompilerIdx < CompilerLines.Length; OtherCompilerIdx++)
{
if(ComparePreprocessedLines(ToolLine, CompilerLines[OtherCompilerIdx]))
{
ToolIdx = ToolIdx + Offset;
CompilerIdx = OtherCompilerIdx;
return true;
}
}
}
// Try to match the compiler line at this offset with a tool line at any equal or smaller offset
if (CompilerIdx + Offset < CompilerLines.Length)
{
string CompilerLine = CompilerLines[CompilerIdx + Offset];
for(int OtherToolIdx = ToolIdx; OtherToolIdx <= ToolIdx + Offset && OtherToolIdx < ToolLines.Length; OtherToolIdx++)
{
if(ComparePreprocessedLines(ToolLines[OtherToolIdx], CompilerLine))
{
ToolIdx = OtherToolIdx;
CompilerIdx = CompilerIdx + Offset;
return true;
}
}
}
}
return false;
}
/// <summary>
/// Compares two preprocessed source file lines, and returns true if they are the same. Ignores leading, trailing, and consecutive whitespace.
/// </summary>
/// <param name="First">First string to compare</param>
/// <param name="Second">Second string to compare</param>
/// <returns>True if the two lines are equivalent</returns>
static bool ComparePreprocessedLines(string First, string Second)
{
// Skip any leading whitespace
int FirstIdx = SkipWhitespace(First, 0);
int SecondIdx = SkipWhitespace(Second, 0);
// Scan to the end of the line
while(FirstIdx < First.Length && SecondIdx < Second.Length)
{
if (IsWhitespace(First[FirstIdx]) && IsWhitespace(Second[SecondIdx]))
{
FirstIdx = SkipWhitespace(First, FirstIdx);
SecondIdx = SkipWhitespace(Second, SecondIdx);
}
else if (First[FirstIdx] == Second[SecondIdx])
{
FirstIdx++;
SecondIdx++;
}
else
{
return false;
}
}
// Skip any trailing whitespace
FirstIdx = SkipWhitespace(First, FirstIdx);
SecondIdx = SkipWhitespace(Second, SecondIdx);
return FirstIdx == First.Length && SecondIdx == Second.Length;
}
/// <summary>
/// Checks whether the given character is whitespace
/// </summary>
/// <param name="Character">The character to check</param>
/// <returns>True if the character is whitespace</returns>
static bool IsWhitespace(char Character)
{
return Character == ' ' || Character == '\t' || Character == '\f';
}
/// <summary>
/// Skips past a sequence of whitespace characters
/// </summary>
/// <param name="Line">The line to check</param>
/// <param name="Idx">Current character index within the line</param>
/// <returns>Index after the run of whitespace characters</returns>
static int SkipWhitespace(string Line, int Idx)
{
while(Idx < Line.Length && IsWhitespace(Line[Idx]))
{
Idx++;
}
return Idx;
}
}
}