Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/Windows/VCToolChain.cs

1395 lines
54 KiB
C#

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;
using Microsoft.Win32;
using System.Text;
namespace UnrealBuildTool
{
public class VCToolChain : UEToolChain
{
public override void RegisterToolChain()
{
// Register this tool chain for both Win64 and Win32
Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.Win64.ToString());
UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.Win64, this);
Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.Win32.ToString());
UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.Win32, this);
}
static void AppendCLArguments_Global(CPPEnvironment CompileEnvironment, VCEnvironment EnvVars, StringBuilder Arguments)
{
// @todo fastubt: Use should be using StringBuilder instead of concatenation in all of these toolchain files. On Mono, string builder is dramatically faster.
// @todo fastubt: We can use '/showIncludes' to accelerate outdatedness checking, as the compiler will discover all indirect includes itself. But, the spew is pretty noisy!
// -> If no files in source file chain have changed, even if the build product is outdated, we can skip using /showIncludes (relies on cache surviving)
// Arguments.Append( " /showIncludes" );
if( WindowsPlatform.bCompileWithClang )
{
// Arguments.Append( " -###" ); // @todo clang: Print Clang command-lines (instead of outputting compile results!)
// @todo clang: We're impersonating the Visual C++ compiler by setting MSC_VER and _MSC_FULL_VER to values that MSVC would set
string VersionString;
switch( WindowsPlatform.Compiler )
{
case WindowsCompiler.VisualStudio2012:
VersionString = "1700";
break;
case WindowsCompiler.VisualStudio2013:
VersionString = "1800";
break;
default:
throw new BuildException( "Unexpected value for WindowsPlatform.Compiler: " + WindowsPlatform.Compiler.ToString() );
}
Arguments.Append(" -fmsc-version=" + VersionString);
Arguments.Append(" /D_MSC_FULL_VER=" + VersionString + "00000");
}
// @todo clang: Clang on Windows doesn't respect "#pragma warning (error: ####)", and we're not passing "/WX", so warnings are not
// treated as errors when compiling on Windows using Clang right now.
if( BuildConfiguration.bEnableCodeAnalysis )
{
Arguments.Append(" /analyze");
// Don't cause analyze warnings to be errors
Arguments.Append(" /analyze:WX-");
// Report functions that use a LOT of stack space. You can lower this value if you
// want more aggressive checking for functions that use a lot of stack memory.
Arguments.Append(" /analyze:stacksize81940");
// Don't bother generating code, only analyze code (may report fewer warnings though.)
//Arguments.Append(" /analyze:only");
}
// Prevents the compiler from displaying its logo for each invocation.
Arguments.Append(" /nologo");
// Enable intrinsic functions.
Arguments.Append(" /Oi");
if( WindowsPlatform.bCompileWithClang )
{
// Tell the Clang compiler whether we want to generate 32-bit code or 64-bit code
if( CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64 )
{
Arguments.Append( " --target=x86_64-pc-windows-msvc" );
}
else
{
Arguments.Append( " --target=x86-pc-windows-msvc" );
}
}
if (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)
{
// Pack struct members on 8-byte boundaries.
Arguments.Append(" /Zp8");
}
else
{
// Pack struct members on 4-byte boundaries.
Arguments.Append(" /Zp4");
}
// Separate functions for linker.
Arguments.Append(" /Gy");
if( !WindowsPlatform.bCompileWithClang ) // @todo clang: These options are not supported with clang-cl yet
{
// Relaxes floating point precision semantics to allow more optimization.
Arguments.Append(" /fp:fast");
}
// Compile into an .obj file, and skip linking.
Arguments.Append(" /c");
// Allow 800% of the default memory allocation limit.
Arguments.Append(" /Zm800");
if( !WindowsPlatform.bCompileWithClang ) // @todo clang: Not supported in clang-cl yet
{
// Allow large object files to avoid hitting the 2^16 section limit when running with -StressTestUnity.
Arguments.Append(" /bigobj");
}
// Disable "The file contains a character that cannot be represented in the current code page" warning for non-US windows.
Arguments.Append(" /wd4819");
if( BuildConfiguration.bUseSharedPCHs )
{
// @todo SharedPCH: Disable warning about PCH defines not matching .cpp defines. We "cheat" these defines a little
// bit to make shared PCHs work. But it's totally safe. Trust us.
Arguments.Append(" /wd4651");
// @todo SharedPCH: Disable warning about redefining *API macros. The PCH header is compiled with various DLLIMPORTs, but
// when a module that uses that PCH header *IS* one of those imports, that module is compiled with EXPORTS, so the macro
// is redefined on the command-line. We need to clobber those defines to make shared PCHs work properly!
Arguments.Append(" /wd4005");
}
// If compiling as a DLL, set the relevant defines
if (CompileEnvironment.Config.bIsBuildingDLL)
{
Arguments.Append(" /D _WINDLL");
}
// When targeting Windows XP with Visual Studio 2012+, we need to tell the compiler to use the older Windows SDK that works
// with Windows XP (http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx)
if( WindowsPlatform.SupportWindowsXP )
{
if( WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2013 )
{
Arguments.Append(" /D_USING_V120_SDK71_");
}
else if (WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2012)
{
Arguments.Append(" /D_USING_V110_SDK71_");
}
}
// Handle Common Language Runtime support (C++/CLI)
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
{
Arguments.Append(" /clr");
// Don't use default lib path, we override it explicitly to use the 4.0 reference assemblies.
Arguments.Append(" /clr:nostdlib");
}
//
// Debug
//
if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug)
{
// Disable compiler optimization.
Arguments.Append(" /Od");
// Favor code size (especially useful for embedded platforms).
Arguments.Append(" /Os");
// Allow inline method expansion unless E&C support is requested
if( !BuildConfiguration.bSupportEditAndContinue )
{
Arguments.Append(" /Ob2");
}
if ((CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32) ||
(CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64))
{
// Runtime stack checks are not allowed when compiling for CLR
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled)
{
Arguments.Append(" /RTCs");
}
}
}
//
// Development and LTCG
//
else
{
// Maximum optimizations if desired.
if( CompileEnvironment.Config.OptimizeCode >= ModuleRules.CodeOptimization.InNonDebugBuilds )
{
Arguments.Append(" /Ox");
if( !WindowsPlatform.bCompileWithClang )
{
// Allow optimized code to be debugged more easily. This makes PDBs a bit larger, but doesn't noticeably affect
// compile times. The executable code is not affected at all by this switch, only the debugging information.
if (EnvVars.CLExeVersion >= new Version("18.0.30723"))
{
// VC2013 Update 3 has a new flag for doing this
Arguments.Append(" /Zo");
}
else
{
Arguments.Append(" /d2Zi+");
}
}
}
// Favor code speed.
Arguments.Append(" /Ot");
// Only omit frame pointers on the PC (which is implied by /Ox) if wanted.
if ( BuildConfiguration.bOmitFramePointers == false
&& ((CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32) ||
(CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)))
{
Arguments.Append(" /Oy-");
}
// Allow inline method expansion
Arguments.Append(" /Ob2");
//
// LTCG
//
if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Shipping)
{
if( BuildConfiguration.bAllowLTCG )
{
// Enable link-time code generation.
Arguments.Append(" /GL");
}
}
}
//
// PC
//
if ((CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32) ||
(CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64))
{
// SSE options are not allowed when using CLR compilation or the 64 bit toolchain
// (both enable SSE2 automatically)
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled &&
CompileEnvironment.Config.Target.Platform != CPPTargetPlatform.Win64)
{
// Allow the compiler to generate SSE2 instructions.
Arguments.Append(" /arch:SSE2");
}
// Prompt the user before reporting internal errors to Microsoft.
Arguments.Append(" /errorReport:prompt");
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled)
{
// Enable C++ exceptions when building with the editor or when building UHT.
if (!WindowsPlatform.bCompileWithClang && // @todo clang: C++ exceptions are not supported with Clang on Windows yet
(CompileEnvironment.Config.bEnableExceptions || UEBuildConfiguration.bBuildEditor || UEBuildConfiguration.bForceEnableExceptions))
{
// Enable C++ exception handling, but not C exceptions.
Arguments.Append(" /EHsc");
}
else
{
// This is required to disable exception handling in VC platform headers.
CompileEnvironment.Config.Definitions.Add("_HAS_EXCEPTIONS=0");
}
}
else
{
// For C++/CLI all exceptions must be left enabled
Arguments.Append(" /EHa");
}
}
// @todo - find a better place for this.
if (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.HTML5)
{
Arguments.Append(" /EHsc");
}
// If enabled, create debug information.
if (CompileEnvironment.Config.bCreateDebugInfo)
{
// Store debug info in .pdb files.
// @todo clang: PDB files are emited from Clang but do not fully work with Visual Studio yet (breakpoints won't hit due to "symbol read error")
if( BuildConfiguration.bUsePDBFiles )
{
// Create debug info suitable for E&C if wanted.
if( BuildConfiguration.bSupportEditAndContinue &&
// We only need to do this in debug as that's the only configuration that supports E&C.
CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug)
{
Arguments.Append(" /ZI");
}
// Regular PDB debug information.
else
{
Arguments.Append(" /Zi");
}
// We need to add this so VS won't lock the PDB file and prevent synchronous updates. This forces serialization through MSPDBSRV.exe.
// See http://msdn.microsoft.com/en-us/library/dn502518.aspx for deeper discussion of /FS switch.
if (BuildConfiguration.bUseIncrementalLinking && WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2013)
{
Arguments.Append(" /FS");
}
}
// Store C7-format debug info in the .obj files, which is faster.
else
{
Arguments.Append(" /Z7");
}
}
// Specify the appropriate runtime library based on the platform and config.
if( CompileEnvironment.Config.bUseStaticCRT )
{
if( CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug && BuildConfiguration.bDebugBuildsActuallyUseDebugCRT )
{
Arguments.Append(" /MTd");
}
else
{
Arguments.Append(" /MT");
}
}
else
{
if( CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug && BuildConfiguration.bDebugBuildsActuallyUseDebugCRT )
{
Arguments.Append(" /MDd");
}
else
{
Arguments.Append(" /MD");
}
}
}
static void AppendCLArguments_CPP( CPPEnvironment CompileEnvironment, StringBuilder Arguments )
{
// Explicitly compile the file as C++.
Arguments.Append(" /TP");
if (!CompileEnvironment.Config.bEnableBufferSecurityChecks)
{
// This will disable buffer security checks (which are enabled by default) that the MS compiler adds around arrays on the stack,
// Which can add some performance overhead, especially in performance intensive code
// Only disable this if you know what you are doing, because it will be disabled for the entire module!
Arguments.Append(" /GS-");
}
// C++/CLI requires that RTTI is left enabled
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled)
{
if (CompileEnvironment.Config.bUseRTTI)
{
// Enable C++ RTTI.
Arguments.Append(" /GR");
}
else
{
// Disable C++ RTTI.
Arguments.Append(" /GR-");
}
}
// Level 4 warnings.
Arguments.Append(" /W4");
if( WindowsPlatform.bCompileWithClang )
{
// Disable specific warnings that cause problems with Clang
// NOTE: These must appear after we set the MSVC warning level
// @todo clang: Ideally we want as few warnings disabled as possible
//
// Allow Microsoft-specific syntax to slide, even though it may be non-standard. Needed for Windows headers.
Arguments.Append(" -Wno-microsoft");
// @todo clang: Kind of a shame to turn these off. We'd like to catch unused variables, but it is tricky with how our assertion macros work.
Arguments.Append(" -Wno-unused-variable");
Arguments.Append(" -Wno-unused-function");
Arguments.Append(" -Wno-unused-private-field");
Arguments.Append(" -Wno-unused-value");
Arguments.Append(" -Wno-inline-new-delete"); // @todo clang: We declare operator new as inline. Clang doesn't seem to like that.
// Sometimes we compare 'this' pointers against nullptr, which Clang warns about by default
Arguments.Append(" -Wno-undefined-bool-conversion" );
// @todo clang: Disabled warnings were copied from MacToolChain for the most part
Arguments.Append(" -Wno-deprecated-declarations");
Arguments.Append(" -Wno-deprecated-writable-strings");
Arguments.Append(" -Wno-deprecated-register");
Arguments.Append(" -Wno-switch-enum");
Arguments.Append(" -Wno-logical-op-parentheses"); // needed for external headers we shan't change
Arguments.Append(" -Wno-null-arithmetic"); // needed for external headers we shan't change
Arguments.Append(" -Wno-deprecated-declarations"); // needed for wxWidgets
Arguments.Append(" -Wno-return-type-c-linkage"); // needed for PhysX
Arguments.Append(" -Wno-ignored-attributes"); // needed for nvtesslib
Arguments.Append(" -Wno-uninitialized");
Arguments.Append(" -Wno-tautological-compare");
Arguments.Append(" -Wno-switch");
Arguments.Append(" -Wno-invalid-offsetof"); // needed to suppress warnings about using offsetof on non-POD types.
}
}
static void AppendCLArguments_C(StringBuilder Arguments)
{
// Explicitly compile the file as C.
Arguments.Append(" /TC");
// Level 0 warnings. Needed for external C projects that produce warnings at higher warning levels.
Arguments.Append(" /W0");
}
static void AppendLinkArguments(LinkEnvironment LinkEnvironment, StringBuilder Arguments)
{
if( WindowsPlatform.bCompileWithClang && WindowsPlatform.bAllowClangLinker )
{
// This tells LLD to run in "Windows emulation" mode, meaning that it will accept MSVC Link arguments
Arguments.Append( " -flavor link" );
// @todo clang: The following static libraries aren't linking correctly with Clang:
// tbbmalloc.lib, zlib_64.lib, libpng_64.lib, freetype2412MT.lib, IlmImf.lib
// LLD: Assertion failed: result.size() == 1, file ..\tools\lld\lib\ReaderWriter\FileArchive.cpp, line 71
}
// Don't create a side-by-side manifest file for the executable.
Arguments.Append(" /MANIFEST:NO");
// Prevents the linker from displaying its logo for each invocation.
Arguments.Append(" /NOLOGO");
if (LinkEnvironment.Config.bCreateDebugInfo)
{
// Output debug info for the linked executable.
Arguments.Append(" /DEBUG");
}
// Prompt the user before reporting internal errors to Microsoft.
Arguments.Append(" /errorReport:prompt");
//
// PC
//
if ((LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32) ||
(LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64))
{
// Set machine type/ architecture to be 64 bit.
if (LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)
{
Arguments.Append(" /MACHINE:x64");
}
// 32 bit executable/ target.
else
{
Arguments.Append(" /MACHINE:x86");
}
{
if (LinkEnvironment.Config.bIsBuildingConsoleApplication)
{
Arguments.Append(" /SUBSYSTEM:CONSOLE");
}
else
{
Arguments.Append(" /SUBSYSTEM:WINDOWS");
}
// When targeting Windows XP in Visual Studio 2012+, we need to tell the linker we are going to support execution
// on that older platform. The compiler defaults to version 6.0+. We'll modify the SUBSYSTEM parameter here.
if( WindowsPlatform.SupportWindowsXP )
{
Arguments.Append(LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64 ? ",5.02" : ",5.01");
}
}
if (LinkEnvironment.Config.bIsBuildingConsoleApplication && !LinkEnvironment.Config.bIsBuildingDLL && !String.IsNullOrEmpty( LinkEnvironment.Config.WindowsEntryPointOverride ) )
{
// Use overridden entry point
Arguments.Append(" /ENTRY:" + LinkEnvironment.Config.WindowsEntryPointOverride);
}
// Allow the OS to load the EXE at different base addresses than its preferred base address.
Arguments.Append(" /FIXED:No");
// Option is only relevant with 32 bit toolchain.
if (LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32)
{
// Disables the 2GB address space limit on 64-bit Windows and 32-bit Windows with /3GB specified in boot.ini
Arguments.Append(" /LARGEADDRESSAWARE");
}
// Explicitly declare that the executable is compatible with Data Execution Prevention.
Arguments.Append(" /NXCOMPAT");
// Set the default stack size.
Arguments.Append(" /STACK:5000000");
// E&C can't use /SAFESEH. Also, /SAFESEH isn't compatible with 64-bit linking
if( !BuildConfiguration.bSupportEditAndContinue &&
LinkEnvironment.Config.Target.Platform != CPPTargetPlatform.Win64)
{
// Generates a table of Safe Exception Handlers. Documentation isn't clear whether they actually mean
// Structured Exception Handlers.
Arguments.Append(" /SAFESEH");
}
// Allow delay-loaded DLLs to be explicitly unloaded.
Arguments.Append(" /DELAY:UNLOAD");
if (LinkEnvironment.Config.bIsBuildingDLL)
{
Arguments.Append(" /DLL");
}
if (LinkEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
{
// DLLs built with managed code aren't allowed to have entry points as they will try to initialize
// complex static variables. Managed code isn't allowed to run during DLLMain, we can't allow
// these variables to be initialized here.
if (LinkEnvironment.Config.bIsBuildingDLL)
{
// NOTE: This appears to only be needed if we want to get call stacks for debugging exit crashes related to the above
// Result += " /NOENTRY /NODEFAULTLIB:nochkclr.obj";
}
}
}
// Don't embed the full PDB path in the binary when building Rocket executables; the location on disk won't match the user's install directory.
if(UnrealBuildTool.BuildingRocket())
{
Arguments.Append(" /PDBALTPATH:%_PDB%");
}
//
// Shipping & LTCG
//
if( BuildConfiguration.bAllowLTCG &&
LinkEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Shipping)
{
// Use link-time code generation.
Arguments.Append(" /LTCG");
// This is where we add in the PGO-Lite linkorder.txt if we are using PGO-Lite
//Result += " /ORDER:@linkorder.txt";
//Result += " /VERBOSE";
}
//
// Shipping binary
//
if (LinkEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Shipping)
{
// Generate an EXE checksum.
Arguments.Append(" /RELEASE");
// Eliminate unreferenced symbols.
Arguments.Append(" /OPT:REF");
// Remove redundant COMDATs.
Arguments.Append(" /OPT:ICF");
}
//
// Regular development binary.
//
else
{
// Keep symbols that are unreferenced.
Arguments.Append(" /OPT:NOREF");
// Disable identical COMDAT folding.
Arguments.Append(" /OPT:NOICF");
}
// Enable incremental linking if wanted.
// NOTE: Don't bother using incremental linking for C++/CLI projects, as that's not supported and the option
// will silently be ignored anyway
if (BuildConfiguration.bUseIncrementalLinking && LinkEnvironment.Config.CLRMode != CPPCLRMode.CLREnabled)
{
Arguments.Append(" /INCREMENTAL");
}
else
{
Arguments.Append(" /INCREMENTAL:NO");
}
// Disable
//LINK : warning LNK4199: /DELAYLOAD:nvtt_64.dll ignored; no imports found from nvtt_64.dll
// type warning as we leverage the DelayLoad option to put third-party DLLs into a
// non-standard location. This requires the module(s) that use said DLL to ensure that it
// is loaded prior to using it.
Arguments.Append(" /ignore:4199");
// Suppress warnings about missing PDB files for statically linked libraries. We often don't want to distribute
// PDB files for these libraries.
Arguments.Append(" /ignore:4099"); // warning LNK4099: PDB '<file>' was not found with '<file>'
}
static void AppendLibArguments(LinkEnvironment LinkEnvironment, StringBuilder Arguments)
{
// Prevents the linker from displaying its logo for each invocation.
Arguments.Append(" /NOLOGO");
// Prompt the user before reporting internal errors to Microsoft.
Arguments.Append(" /errorReport:prompt");
//
// PC
//
if (LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32 || LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)
{
// Set machine type/ architecture to be 64 bit.
if (LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)
{
Arguments.Append(" /MACHINE:x64");
}
// 32 bit executable/ target.
else
{
Arguments.Append(" /MACHINE:x86");
}
{
if (LinkEnvironment.Config.bIsBuildingConsoleApplication)
{
Arguments.Append(" /SUBSYSTEM:CONSOLE");
}
else
{
Arguments.Append(" /SUBSYSTEM:WINDOWS");
}
// When targeting Windows XP in Visual Studio 2012+, we need to tell the linker we are going to support execution
// on that older platform. The compiler defaults to version 6.0+. We'll modify the SUBSYSTEM parameter here.
if( WindowsPlatform.SupportWindowsXP )
{
Arguments.Append(LinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64 ? ",5.02" : ",5.01");
}
}
}
//
// Shipping & LTCG
//
if (LinkEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Shipping)
{
// Use link-time code generation.
Arguments.Append(" /LTCG");
}
}
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName)
{
var EnvVars = VCEnvironment.SetEnvironment(CompileEnvironment.Config.Target.Platform);
StringBuilder Arguments = new StringBuilder();
AppendCLArguments_Global(CompileEnvironment, EnvVars, Arguments);
// Add include paths to the argument list.
foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths)
{
Arguments.AppendFormat(" /I \"{0}\"", IncludePath);
}
foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths)
{
if( WindowsPlatform.bCompileWithClang )
{
// @todo Clang: Clang uses a special command-line syntax for system headers. This is used for two reasons. The first is that Clang will automatically
// suppress compiler warnings in headers found in these directories, such as the DirectX SDK headers. The other reason this is important is in the case
// where there the same header include path is passed as both a regular include path and a system include path (extracted from INCLUDE environment). In
// this case Clang will ignore any earlier occurrence of the include path, preventing a system header include path from overriding a different system
// include path set later on by a module. NOTE: When passing "-Xclang", these options will always appear at the end of the command-line string, meaning
// they will be forced to appear *after* all environment-variable-extracted includes. This is technically okay though.
Arguments.AppendFormat(" -Xclang -internal-isystem -Xclang \"{0}\"", IncludePath);
}
else
{
Arguments.AppendFormat(" /I \"{0}\"", IncludePath);
}
}
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
{
// Add .NET framework assembly paths. This is needed so that C++/CLI projects
// can reference assemblies with #using, without having to hard code a path in the
// .cpp file to the assembly's location.
foreach (string AssemblyPath in CompileEnvironment.Config.SystemDotNetAssemblyPaths)
{
Arguments.AppendFormat(" /AI \"{0}\"", AssemblyPath);
}
// Add explicit .NET framework assembly references
foreach (string AssemblyName in CompileEnvironment.Config.FrameworkAssemblyDependencies)
{
Arguments.AppendFormat( " /FU \"{0}\"", AssemblyName );
}
// Add private assembly references
foreach( PrivateAssemblyInfo CurAssemblyInfo in CompileEnvironment.PrivateAssemblyDependencies )
{
Arguments.AppendFormat( " /FU \"{0}\"", CurAssemblyInfo.FileItem.AbsolutePath );
}
}
// Add preprocessor definitions to the argument list.
foreach (string Definition in CompileEnvironment.Config.Definitions)
{
// Escape all quotation marks so that they get properly passed with the command line.
var DefinitionArgument = Definition.Contains("\"") ? Definition.Replace("\"", "\\\"") : Definition;
Arguments.AppendFormat(" /D \"{0}\"", DefinitionArgument);
}
var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform);
// Create a compile action for each source file.
CPPOutput Result = new CPPOutput();
foreach (FileItem SourceFile in SourceFiles)
{
Action CompileAction = new Action(ActionType.Compile);
CompileAction.CommandDescription = "Compile";
StringBuilder FileArguments = new StringBuilder();
bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C";
// Add the C++ source file and its included files to the prerequisite item list.
AddPrerequisiteSourceFile( Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems );
// If this is a CLR file then make sure our dependent assemblies are added as prerequisites
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
{
foreach( PrivateAssemblyInfo CurPrivateAssemblyDependency in CompileEnvironment.PrivateAssemblyDependencies )
{
CompileAction.PrerequisiteItems.Add( CurPrivateAssemblyDependency.FileItem );
}
}
if( WindowsPlatform.bCompileWithClang )
{
CompileAction.OutputEventHandler = new DataReceivedEventHandler( ClangCompilerOutputFormatter );
}
bool bEmitsObjectFile = true;
if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
{
// Generate a CPP File that just includes the precompiled header.
string PCHCPPFilename = "PCH." + ModuleName + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename) + ".cpp";
string PCHCPPPath = Path.Combine(CompileEnvironment.Config.OutputDirectory, PCHCPPFilename);
FileItem PCHCPPFile = FileItem.CreateIntermediateTextFile(
PCHCPPPath,
string.Format("#include \"{0}\"\r\n", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename)
);
// Make sure the original source directory the PCH header file existed in is added as an include
// path -- it might be a private PCH header and we need to make sure that its found!
string OriginalPCHHeaderDirectory = Path.GetDirectoryName( SourceFile.AbsolutePath );
FileArguments.AppendFormat(" /I \"{0}\"", OriginalPCHHeaderDirectory);
var PrecompiledFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Win64].GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader);
// Add the precompiled header file to the produced items list.
FileItem PrecompiledHeaderFile = FileItem.GetItemByPath(
Path.Combine(
CompileEnvironment.Config.OutputDirectory,
Path.GetFileName(SourceFile.AbsolutePath) + PrecompiledFileExtension
)
);
CompileAction.ProducedItems.Add(PrecompiledHeaderFile);
Result.PrecompiledHeaderFile = PrecompiledHeaderFile;
// @todo clang: Ideally clang-cl would support the regular MSVC arguments for PCH files, and we wouldn't need to do this manually
if( WindowsPlatform.bCompileWithClang )
{
// Tell Clang to generate a PCH header
FileArguments.Append(" -Xclang -x -Xclang c++-header"); // @todo clang: Doesn't work due to Clang-cl overriding us at the end of the command-line (see -### option output)
FileArguments.AppendFormat(" /Fo\"{0}\"", PrecompiledHeaderFile.AbsolutePath);
// Clang PCH generation doesn't create an .obj file to link in, unlike MSVC
bEmitsObjectFile = false;
}
else
{
// Add the parameters needed to compile the precompiled header file to the command-line.
FileArguments.AppendFormat(" /Yc\"{0}\"", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
FileArguments.AppendFormat(" /Fp\"{0}\"", PrecompiledHeaderFile.AbsolutePath);
// If we're creating a PCH that will be used to compile source files for a library, we need
// the compiled modules to retain a reference to PCH's module, so that debugging information
// will be included in the library. This is also required to avoid linker warning "LNK4206"
// when linking an application that uses this library.
if (CompileEnvironment.Config.bIsBuildingLibrary)
{
// NOTE: The symbol name we use here is arbitrary, and all that matters is that it is
// unique per PCH module used in our library
string FakeUniquePCHSymbolName = Path.GetFileNameWithoutExtension(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
FileArguments.AppendFormat(" /Yl{0}", FakeUniquePCHSymbolName);
}
}
FileArguments.AppendFormat(" \"{0}\"", PCHCPPFile.AbsolutePath);
CompileAction.StatusDescription = PCHCPPFilename;
}
else
{
if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
{
CompileAction.bIsUsingPCH = true;
CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile);
if( WindowsPlatform.bCompileWithClang )
{
// NOTE: With Clang, PCH headers are ALWAYS forcibly included!
// NOTE: This needs to be before the other include paths to ensure Clang uses it instead of the source header file.
FileArguments.AppendFormat(" /FI\"{0}\"", Path.ChangeExtension(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath, null));
}
else
{
FileArguments.AppendFormat(" /Yu\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode);
FileArguments.AppendFormat(" /Fp\"{0}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath);
// Is it unsafe to always force inclusion? Clang is doing it, and .generated.cpp files
// won't work otherwise, because they're not located in the context of the module,
// so they can't access the module's PCH without an absolute path.
//if( CompileEnvironment.Config.bForceIncludePrecompiledHeader )
{
// Force include the precompiled header file. This is needed because we may have selected a
// precompiled header that is different than the first direct include in the C++ source file, but
// we still need to make sure that our precompiled header is the first thing included!
FileArguments.AppendFormat( " /FI\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode);
}
}
}
// Add the source file path to the command-line.
FileArguments.AppendFormat(" \"{0}\"", SourceFile.AbsolutePath);
CompileAction.StatusDescription = Path.GetFileName( SourceFile.AbsolutePath );
}
if( bEmitsObjectFile )
{
var ObjectFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Win64].GetBinaryExtension(UEBuildBinaryType.Object);
// Add the object file to the produced item list.
FileItem ObjectFile = FileItem.GetItemByPath(
Path.Combine(
CompileEnvironment.Config.OutputDirectory,
Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension
)
);
CompileAction.ProducedItems.Add(ObjectFile);
Result.ObjectFiles.Add(ObjectFile);
FileArguments.AppendFormat(" /Fo\"{0}\"", ObjectFile.AbsolutePath);
}
// Create PDB files if we were configured to do that.
//
// Also, when debug info is off and XGE is enabled, force PDBs, otherwise it will try to share
// a PDB file, which causes PCH creation to be serial rather than parallel (when debug info is disabled)
// --> See https://udn.epicgames.com/lists/showpost.php?id=50619&list=unprog3
if (BuildConfiguration.bUsePDBFiles ||
(BuildConfiguration.bAllowXGE && !CompileEnvironment.Config.bCreateDebugInfo))
{
string PDBFileName;
bool bActionProducesPDB = false;
// All files using the same PCH are required to share the same PDB that was used when compiling the PCH
if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
{
PDBFileName = "PCH." + ModuleName + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
}
// Files creating a PCH use a PDB per file.
else if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
{
PDBFileName = "PCH." + ModuleName + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
bActionProducesPDB = true;
}
// Ungrouped C++ files use a PDB per file.
else if( !bIsPlainCFile )
{
PDBFileName = Path.GetFileName( SourceFile.AbsolutePath );
bActionProducesPDB = true;
}
// Group all plain C files that doesn't use PCH into the same PDB
else
{
PDBFileName = "MiscPlainC";
}
{
// Specify the PDB file that the compiler should write to.
FileItem PDBFile = FileItem.GetItemByPath(
Path.Combine(
CompileEnvironment.Config.OutputDirectory,
PDBFileName + ".pdb"
)
);
FileArguments.AppendFormat(" /Fd\"{0}\"", PDBFile.AbsolutePath);
// Only use the PDB as an output file if we want PDBs and this particular action is
// the one that produces the PDB (as opposed to no debug info, where the above code
// is needed, but not the output PDB, or when multiple files share a single PDB, so
// only the action that generates it should count it as output directly)
if( BuildConfiguration.bUsePDBFiles && bActionProducesPDB )
{
CompileAction.ProducedItems.Add(PDBFile);
Result.DebugDataFiles.Add(PDBFile);
}
}
}
// Add C or C++ specific compiler arguments.
if (bIsPlainCFile)
{
AppendCLArguments_C(FileArguments);
}
else
{
AppendCLArguments_CPP( CompileEnvironment, FileArguments );
}
CompileAction.WorkingDirectory = Path.GetFullPath(".");
CompileAction.CommandPath = EnvVars.CompilerPath;
if( !WindowsPlatform.bCompileWithClang )
{
CompileAction.bIsVCCompiler = true;
}
CompileAction.CommandArguments = Arguments.ToString() + FileArguments.ToString() + CompileEnvironment.Config.AdditionalArguments;
if( CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create )
{
Log.TraceVerbose("Creating PCH: " + CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
Log.TraceVerbose(" Command: " + CompileAction.CommandArguments);
}
else
{
Log.TraceVerbose(" Compiling: " + CompileAction.StatusDescription);
Log.TraceVerbose(" Command: " + CompileAction.CommandArguments);
}
if( WindowsPlatform.bCompileWithClang )
{
// Clang doesn't print the file names by default, so we'll do it ourselves
CompileAction.bShouldOutputStatusDescription = true;
}
else
{
// VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves
CompileAction.bShouldOutputStatusDescription = false;
}
// Don't farm out creation of precompiled headers as it is the critical path task.
CompileAction.bCanExecuteRemotely =
CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create ||
BuildConfiguration.bAllowRemotelyCompiledPCHs
;
// @todo: XGE has problems remote compiling C++/CLI files that use .NET Framework 4.0
if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
{
CompileAction.bCanExecuteRemotely = false;
}
}
return Result;
}
public override CPPOutput CompileRCFiles(UEBuildTarget Target, CPPEnvironment Environment, List<FileItem> RCFiles)
{
var EnvVars = VCEnvironment.SetEnvironment(Environment.Config.Target.Platform);
CPPOutput Result = new CPPOutput();
var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Environment.Config.Target.Platform);
foreach (FileItem RCFile in RCFiles)
{
Action CompileAction = new Action(ActionType.Compile);
CompileAction.CommandDescription = "Resource";
CompileAction.WorkingDirectory = Path.GetFullPath(".");
CompileAction.CommandPath = EnvVars.ResourceCompilerPath;
CompileAction.StatusDescription = Path.GetFileName(RCFile.AbsolutePath);
if( WindowsPlatform.bCompileWithClang )
{
CompileAction.OutputEventHandler = new DataReceivedEventHandler( ClangCompilerOutputFormatter );
}
// Suppress header spew
CompileAction.CommandArguments += " /nologo";
// If we're compiling for 64-bit Windows, also add the _WIN64 definition to the resource
// compiler so that we can switch on that in the .rc file using #ifdef.
if (Environment.Config.Target.Platform == CPPTargetPlatform.Win64)
{
CompileAction.CommandArguments += " /D _WIN64";
}
// When targeting Windows XP with Visual Studio 2012+, we need to tell the compiler to use the older Windows SDK that works
// with Windows XP (http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx)
if (WindowsPlatform.SupportWindowsXP)
{
if (WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2013)
{
CompileAction.CommandArguments += " /D_USING_V120_SDK71_";
}
else if (WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2012)
{
CompileAction.CommandArguments += " /D_USING_V110_SDK71_";
}
}
// Language
CompileAction.CommandArguments += " /l 0x409";
// Include paths.
foreach (string IncludePath in Environment.Config.CPPIncludeInfo.IncludePaths)
{
CompileAction.CommandArguments += string.Format( " /i \"{0}\"", IncludePath );
}
// System include paths.
foreach( var SystemIncludePath in Environment.Config.CPPIncludeInfo.SystemIncludePaths )
{
CompileAction.CommandArguments += string.Format( " /i \"{0}\"", SystemIncludePath );
}
// Preprocessor definitions.
foreach (string Definition in Environment.Config.Definitions)
{
CompileAction.CommandArguments += string.Format(" /d \"{0}\"", Definition);
}
// Add the RES file to the produced item list.
FileItem CompiledResourceFile = FileItem.GetItemByPath(
Path.Combine(
Environment.Config.OutputDirectory,
Path.GetFileName(RCFile.AbsolutePath) + ".res"
)
);
CompileAction.ProducedItems.Add(CompiledResourceFile);
CompileAction.CommandArguments += string.Format(" /fo \"{0}\"", CompiledResourceFile.AbsolutePath);
Result.ObjectFiles.Add(CompiledResourceFile);
// Add the RC file as a prerequisite of the action.
CompileAction.CommandArguments += string.Format(" \"{0}\"", RCFile.AbsolutePath);
// Add the C++ source file and its included files to the prerequisite item list.
AddPrerequisiteSourceFile( Target, BuildPlatform, Environment, RCFile, CompileAction.PrerequisiteItems );
}
return Result;
}
public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
{
var EnvVars = VCEnvironment.SetEnvironment(LinkEnvironment.Config.Target.Platform);
if (LinkEnvironment.Config.bIsBuildingDotNetAssembly)
{
return FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePath);
}
bool bIsBuildingLibrary = LinkEnvironment.Config.bIsBuildingLibrary || bBuildImportLibraryOnly;
bool bIncludeDependentLibrariesInLibrary = bIsBuildingLibrary && LinkEnvironment.Config.bIncludeDependentLibrariesInLibrary;
// Get link arguments.
StringBuilder Arguments = new StringBuilder();
if(bIsBuildingLibrary)
{
AppendLibArguments(LinkEnvironment, Arguments);
}
else
{
AppendLinkArguments(LinkEnvironment, Arguments);
}
// If we're only building an import library, add the '/DEF' option that tells the LIB utility
// to simply create a .LIB file and .EXP file, and don't bother validating imports
if (bBuildImportLibraryOnly)
{
Arguments.Append(" /DEF");
// Ensure that the import library references the correct filename for the linked binary.
Arguments.AppendFormat(" /NAME:\"{0}\"", Path.GetFileName(LinkEnvironment.Config.OutputFilePath));
}
// Add delay loaded DLLs.
if (!bIsBuildingLibrary)
{
// Delay-load these DLLs.
foreach (string DelayLoadDLL in LinkEnvironment.Config.DelayLoadDLLs)
{
Arguments.AppendFormat(" /DELAYLOAD:\"{0}\"", DelayLoadDLL);
}
}
// @todo UE4 DLL: Why do I need LIBPATHs to build only export libraries with /DEF? (tbbmalloc.lib)
if (!LinkEnvironment.Config.bIsBuildingLibrary || (LinkEnvironment.Config.bIsBuildingLibrary && bIncludeDependentLibrariesInLibrary))
{
// Add the library paths to the argument list.
foreach (string LibraryPath in LinkEnvironment.Config.LibraryPaths)
{
Arguments.AppendFormat(" /LIBPATH:\"{0}\"", LibraryPath);
}
// Add the excluded default libraries to the argument list.
foreach (string ExcludedLibrary in LinkEnvironment.Config.ExcludedLibraries)
{
Arguments.AppendFormat(" /NODEFAULTLIB:\"{0}\"", ExcludedLibrary);
}
}
// For targets that are cross-referenced, we don't want to write a LIB file during the link step as that
// file will clobber the import library we went out of our way to generate during an earlier step. This
// file is not needed for our builds, but there is no way to prevent MSVC from generating it when
// linking targets that have exports. We don't want this to clobber our LIB file and invalidate the
// existing timstamp, so instead we simply emit it with a different name
string ImportLibraryFilePath = Path.Combine( LinkEnvironment.Config.IntermediateDirectory,
Path.GetFileNameWithoutExtension(LinkEnvironment.Config.OutputFilePath) + ".lib" );
if (LinkEnvironment.Config.bIsCrossReferenced && !bBuildImportLibraryOnly)
{
ImportLibraryFilePath += ".suppressed";
}
FileItem OutputFile;
if (bBuildImportLibraryOnly)
{
OutputFile = FileItem.GetItemByPath(ImportLibraryFilePath);
}
else
{
OutputFile = FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePath);
OutputFile.bNeedsHotReloadNumbersDLLCleanUp = LinkEnvironment.Config.bIsBuildingDLL;
}
List<FileItem> ProducedItems = new List<FileItem>();
ProducedItems.Add(OutputFile);
List<FileItem> PrerequisiteItems = new List<FileItem>();
// Add the input files to a response file, and pass the response file on the command-line.
List<string> InputFileNames = new List<string>();
foreach (FileItem InputFile in LinkEnvironment.InputFiles)
{
InputFileNames.Add(string.Format("\"{0}\"", InputFile.AbsolutePath));
PrerequisiteItems.Add(InputFile);
}
if (!bBuildImportLibraryOnly)
{
// Add input libraries as prerequisites, too!
foreach (FileItem InputLibrary in LinkEnvironment.InputLibraries)
{
InputFileNames.Add(string.Format("\"{0}\"", InputLibrary.AbsolutePath));
PrerequisiteItems.Add(InputLibrary);
}
}
if (!bIsBuildingLibrary || (LinkEnvironment.Config.bIsBuildingLibrary && bIncludeDependentLibrariesInLibrary))
{
foreach (string AdditionalLibrary in LinkEnvironment.Config.AdditionalLibraries)
{
InputFileNames.Add(string.Format("\"{0}\"", AdditionalLibrary));
// If the library file name has a relative path attached (rather than relying on additional
// lib directories), then we'll add it to our prerequisites list. This will allow UBT to detect
// when the binary needs to be relinked because a dependent external library has changed.
//if( !String.IsNullOrEmpty( Path.GetDirectoryName( AdditionalLibrary ) ) )
{
PrerequisiteItems.Add(FileItem.GetItemByPath(AdditionalLibrary));
}
}
}
// Create a response file for the linker
string ResponseFileName = GetResponseFileName( LinkEnvironment, OutputFile );
// Never create response files when we are only generating IntelliSense data
if( !ProjectFileGenerator.bGenerateProjectFiles )
{
ResponseFile.Create( ResponseFileName, InputFileNames );
}
Arguments.AppendFormat(" @\"{0}\"", ResponseFileName);
// Add the output file to the command-line.
Arguments.AppendFormat(" /OUT:\"{0}\"", OutputFile.AbsolutePath);
if (bBuildImportLibraryOnly || (LinkEnvironment.Config.bHasExports && !bIsBuildingLibrary))
{
// An export file is written to the output directory implicitly; add it to the produced items list.
string ExportFilePath = Path.ChangeExtension(ImportLibraryFilePath, ".exp");
FileItem ExportFile = FileItem.GetItemByPath(ExportFilePath);
ProducedItems.Add(ExportFile);
}
if (!bIsBuildingLibrary)
{
// Xbox 360 LTCG does not seem to produce those.
if (LinkEnvironment.Config.bHasExports &&
(LinkEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Shipping))
{
// Write the import library to the output directory for nFringe support.
FileItem ImportLibraryFile = FileItem.GetItemByPath(ImportLibraryFilePath);
Arguments.AppendFormat(" /IMPLIB:\"{0}\"", ImportLibraryFilePath);
ProducedItems.Add(ImportLibraryFile);
}
if (LinkEnvironment.Config.bCreateDebugInfo)
{
// Write the PDB file to the output directory.
{
string PDBFilePath = Path.Combine(LinkEnvironment.Config.OutputDirectory, Path.GetFileNameWithoutExtension(OutputFile.AbsolutePath) + ".pdb");
FileItem PDBFile = FileItem.GetItemByPath(PDBFilePath);
Arguments.AppendFormat(" /PDB:\"{0}\"", PDBFilePath);
ProducedItems.Add(PDBFile);
}
// Write the MAP file to the output directory.
#if false
if (true)
{
string MAPFilePath = Path.Combine(LinkEnvironment.Config.OutputDirectory, Path.GetFileNameWithoutExtension(OutputFile.AbsolutePath) + ".map");
FileItem MAPFile = FileItem.GetItemByPath(MAPFilePath);
LinkAction.CommandArguments += string.Format(" /MAP:\"{0}\"", MAPFilePath);
LinkAction.ProducedItems.Add(MAPFile);
}
#endif
}
// Add the additional arguments specified by the environment.
Arguments.Append(LinkEnvironment.Config.AdditionalArguments);
}
// Create an action that invokes the linker.
Action LinkAction = new Action(ActionType.Link);
LinkAction.CommandDescription = "Link";
LinkAction.WorkingDirectory = Path.GetFullPath(".");
LinkAction.CommandPath = bIsBuildingLibrary ? EnvVars.LibraryLinkerPath : EnvVars.LinkerPath;
LinkAction.CommandArguments = Arguments.ToString();
LinkAction.ProducedItems .AddRange(ProducedItems);
LinkAction.PrerequisiteItems.AddRange(PrerequisiteItems);
LinkAction.StatusDescription = Path.GetFileName(OutputFile.AbsolutePath);
if( WindowsPlatform.bCompileWithClang )
{
LinkAction.OutputEventHandler = new DataReceivedEventHandler( ClangCompilerOutputFormatter );
}
// Tell the action that we're building an import library here and it should conditionally be
// ignored as a prerequisite for other actions
LinkAction.bProducesImportLibrary = bBuildImportLibraryOnly || LinkEnvironment.Config.bIsBuildingDLL;
// Only execute linking on the local PC.
LinkAction.bCanExecuteRemotely = false;
Log.TraceVerbose( " Linking: " + LinkAction.StatusDescription );
Log.TraceVerbose( " Command: " + LinkAction.CommandArguments );
return OutputFile;
}
public override void CompileCSharpProject(CSharpEnvironment CompileEnvironment, string ProjectFileName, string DestinationFile)
{
// Initialize environment variables required for spawned tools.
var EnvVars = VCEnvironment.SetEnvironment(CompileEnvironment.EnvironmentTargetPlatform);
var BuildProjectAction = new Action(ActionType.BuildProject);
// Specify the source file (prerequisite) for the action
var ProjectFileItem = FileItem.GetExistingItemByPath(ProjectFileName);
if (ProjectFileItem == null)
{
throw new BuildException("Expected C# project file {0} to exist.", ProjectFileName);
}
// Add the project and the files contained to the prerequisites.
BuildProjectAction.PrerequisiteItems.Add(ProjectFileItem);
var ProjectFile = new VCSharpProjectFile( Utils.MakePathRelativeTo( ProjectFileName, ProjectFileGenerator.MasterProjectRelativePath ) );
var ProjectPreReqs = ProjectFile.GetCSharpDependencies();
var ProjectFolder = Path.GetDirectoryName(ProjectFileName);
foreach( string ProjectPreReqRelativePath in ProjectPreReqs )
{
string ProjectPreReqAbsolutePath = Path.Combine( ProjectFolder, ProjectPreReqRelativePath );
var ProjectPreReqFileItem = FileItem.GetExistingItemByPath(ProjectPreReqAbsolutePath);
if( ProjectPreReqFileItem == null )
{
throw new BuildException("Expected C# dependency {0} to exist.", ProjectPreReqAbsolutePath);
}
BuildProjectAction.PrerequisiteItems.Add(ProjectPreReqFileItem);
}
// We might be able to distribute this safely, but it doesn't take any time.
BuildProjectAction.bCanExecuteRemotely = false;
// Setup execution via MSBuild.
BuildProjectAction.WorkingDirectory = Path.GetFullPath(".");
BuildProjectAction.StatusDescription = Path.GetFileName(ProjectFileName);
BuildProjectAction.CommandPath = EnvVars.MSBuildPath;
if (CompileEnvironment.TargetConfiguration == CSharpTargetConfiguration.Debug)
{
BuildProjectAction.CommandArguments = " /target:rebuild /property:Configuration=Debug";
}
else
{
BuildProjectAction.CommandArguments = " /target:rebuild /property:Configuration=Development";
}
// Be less verbose
BuildProjectAction.CommandArguments += " /nologo /verbosity:minimal";
// Add project
BuildProjectAction.CommandArguments += String.Format(" \"{0}\"", ProjectFileItem.AbsolutePath);
// Specify the output files.
string PDBFilePath = Path.Combine( Path.GetDirectoryName(DestinationFile), Path.GetFileNameWithoutExtension(DestinationFile) + ".pdb" );
FileItem PDBFile = FileItem.GetItemByPath( PDBFilePath );
BuildProjectAction.ProducedItems.Add( FileItem.GetItemByPath(DestinationFile) );
BuildProjectAction.ProducedItems.Add( PDBFile );
}
/** Gets the default include paths for the given platform. */
public static string GetVCIncludePaths(CPPTargetPlatform Platform)
{
Debug.Assert(Platform == CPPTargetPlatform.Win32 || Platform == CPPTargetPlatform.Win64);
// Make sure we've got the environment variables set up for this target
VCEnvironment.SetEnvironment(Platform);
// Also add any include paths from the INCLUDE environment variable. MSVC is not necessarily running with an environment that
// matches what UBT extracted from the vcvars*.bat using SetEnvironmentVariablesFromBatchFile(). We'll use the variables we
// extracted to populate the project file's list of include paths
// @todo projectfiles: Should we only do this for VC++ platforms?
var IncludePaths = Environment.GetEnvironmentVariable("INCLUDE");
if (!String.IsNullOrEmpty(IncludePaths) && !IncludePaths.EndsWith(";"))
{
IncludePaths += ";";
}
return IncludePaths;
}
public override void AddFilesToManifest(ref FileManifest Manifest, UEBuildBinary Binary)
{
// ok, this is pretty awful, we want the import libraries that go with the editor, only on the PC
if (UnrealBuildTool.BuildingRocket() &&
Path.GetFileNameWithoutExtension(Binary.Config.OutputFilePath).StartsWith("UE4Editor-", StringComparison.InvariantCultureIgnoreCase) &&
Path.GetExtension(Binary.Config.OutputFilePath).EndsWith("dll", StringComparison.InvariantCultureIgnoreCase) &&
Binary.Config.Type == UEBuildBinaryType.DynamicLinkLibrary)
{
// ok, this is pretty awful, we want the import libraries that go with the editor, only on the PC
Manifest.AddBinaryNames(Path.Combine(Binary.Config.IntermediateDirectory, Path.GetFileNameWithoutExtension(Binary.Config.OutputFilePath) + ".lib"), "");
}
}
/** Formats compiler output from Clang so that it is clickable in Visual Studio */
protected static void ClangCompilerOutputFormatter(object sender, DataReceivedEventArgs e)
{
var Output = e.Data;
if (Output == null)
{
return;
}
// @todo clang: Convert relative includes to absolute files so they'll be clickable
Log.TraceInformation(Output);
}
};
}