2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-08-26 06:55:48 -04:00
using System ;
using System.Collections.Generic ;
using System.Text.RegularExpressions ;
using System.Diagnostics ;
using System.IO ;
using Microsoft.Win32 ;
using System.Text ;
namespace UnrealBuildTool
{
class VCEnvironment
{
public readonly CPPTargetPlatform Platform ; // The platform the envvars have been initialized for
public readonly string BaseVSToolPath ; // The path to Visual Studio's /Common7/Tools directory.
2015-08-05 15:54:57 -04:00
public readonly string VSToolPath32Bit ; // The path to the 32bit platform tool binaries.
public readonly string VSToolPath64Bit ; // The path to the 64bit platform tool binaries.
2014-08-26 06:55:48 -04:00
public readonly string WindowsSDKDir ; // Installation folder of the Windows SDK, e.g. C:\Program Files\Microsoft SDKs\Windows\v6.0A\
2015-05-05 15:32:10 -04:00
public readonly string WindowsSDKExtensionDir ; // Installation folder of the Windows SDK Extensions, e.g. C:\Program Files (x86)\Windows SDKs\10
public readonly string NetFxSDKExtensionDir ; // Installation folder of the NetFx SDK, since that is split out from platform SDKs >= v10
public readonly Version WindowsSDKExtensionHeaderLibVersion ; // 10.0.9910.0 for instance...
2014-08-26 06:55:48 -04:00
public readonly string CompilerPath ; // The path to the linker for linking executables
public readonly Version CLExeVersion ; // The version of cl.exe we're running
public readonly string LinkerPath ; // The path to the linker for linking executables
public readonly string LibraryLinkerPath ; // The path to the linker for linking libraries
public readonly string ResourceCompilerPath ; // The path to the resource compiler
2014-09-11 12:34:13 -04:00
private string _MSBuildPath = null ;
public string MSBuildPath // The path to MSBuild
{
get
{
if ( _MSBuildPath = = null )
{
_MSBuildPath = GetMSBuildToolPath ( ) ;
}
return _MSBuildPath ;
}
}
2014-08-26 06:55:48 -04:00
/ * *
* Initializes environment variables required by toolchain . Different for 32 and 64 bit .
* /
public static VCEnvironment SetEnvironment ( CPPTargetPlatform Platform )
{
if ( EnvVars ! = null & & EnvVars . Platform = = Platform )
{
return EnvVars ;
}
EnvVars = new VCEnvironment ( Platform ) ;
return EnvVars ;
}
private VCEnvironment ( CPPTargetPlatform InPlatform )
{
Platform = InPlatform ;
// If Visual Studio is not installed, the Windows SDK path will be used, which also happens to be the same
// directory. (It installs the toolchain into the folder where Visual Studio would have installed it to).
BaseVSToolPath = WindowsPlatform . GetVSComnToolsPath ( ) ;
if ( string . IsNullOrEmpty ( BaseVSToolPath ) )
{
2015-05-05 15:32:10 -04:00
throw new BuildException ( "Visual Studio 2012, 2013 or 2015 must be installed in order to build this target." ) ;
2014-08-26 06:55:48 -04:00
}
2015-01-28 08:26:51 -05:00
WindowsSDKDir = FindWindowsSDKInstallationFolder ( Platform ) ;
2015-05-05 15:32:10 -04:00
WindowsSDKExtensionDir = FindWindowsSDKExtensionInstallationFolder ( ) ;
NetFxSDKExtensionDir = FindNetFxSDKExtensionInstallationFolder ( ) ;
WindowsSDKExtensionHeaderLibVersion = FindWindowsSDKExtensionLatestVersion ( WindowsSDKExtensionDir ) ;
2015-08-05 15:54:57 -04:00
VSToolPath32Bit = GetVSToolPath32Bit ( BaseVSToolPath ) ;
VSToolPath64Bit = GetVSToolPath64Bit ( BaseVSToolPath ) ;
// Compile using 64 bit tools for 64 bit targets, and 32 for 32.
string CompilerVSToolPath = ( Platform = = CPPTargetPlatform . Win64 | | Platform = = CPPTargetPlatform . UWP ) ? VSToolPath64Bit : VSToolPath32Bit ;
// Regardless of the target, if we're linking on a 64 bit machine, we want to use the 64 bit linker (it's faster than the 32 bit linker and can handle large linking jobs)
string LinkerVSToolPath = VSToolPath64Bit ;
CompilerPath = GetCompilerToolPath ( CompilerVSToolPath ) ;
2014-08-26 06:55:48 -04:00
CLExeVersion = FindCLExeVersion ( CompilerPath ) ;
2015-08-05 15:54:57 -04:00
LinkerPath = GetLinkerToolPath ( LinkerVSToolPath ) ;
LibraryLinkerPath = GetLibraryLinkerToolPath ( LinkerVSToolPath ) ;
2015-05-05 15:32:10 -04:00
ResourceCompilerPath = GetResourceCompilerToolPath ( Platform ) ;
2014-08-26 06:55:48 -04:00
2015-05-28 12:43:44 -04:00
// We ensure an extra trailing slash because of a user getting an odd error where the paths seemed to get concatenated wrongly:
//
// C:\Programme\Microsoft Visual Studio 12.0\Common7\Tools../../VC/bin/x86_amd64/vcvarsx86_amd64.bat
//
// https://answers.unrealengine.com/questions/233640/unable-to-create-project-files-for-48-preview-3.html
//
bool bUse64BitCompiler = Platform = = CPPTargetPlatform . Win64 | | Platform = = CPPTargetPlatform . UWP ;
string BaseToolPathWithTrailingSlash = BaseVSToolPath . TrimEnd ( Path . DirectorySeparatorChar ) + Path . DirectorySeparatorChar ;
string VCVarsBatchFile = Path . Combine ( BaseToolPathWithTrailingSlash , bUse64BitCompiler ? @"..\..\VC\bin\x86_amd64\vcvarsx86_amd64.bat" : "vsvars32.bat" ) ;
2015-05-12 12:00:23 -04:00
if ( Platform = = CPPTargetPlatform . UWP & & UWPPlatform . bBuildForStore )
2015-05-05 15:32:10 -04:00
{
Utils . SetEnvironmentVariablesFromBatchFile ( VCVarsBatchFile , "store" ) ;
}
else
{
Utils . SetEnvironmentVariablesFromBatchFile ( VCVarsBatchFile ) ;
}
2014-08-26 06:55:48 -04:00
// When targeting Windows XP on Visual Studio 2012+, we need to override the Windows SDK include and lib path set
// by the batch file environment (http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx)
2015-01-28 08:26:51 -05:00
if ( WindowsPlatform . IsWindowsXPSupported ( ) )
2014-08-26 06:55:48 -04:00
{
// Lib and bin folders have a x64 subfolder for 64 bit development.
var ConfigSuffix = ( Platform = = CPPTargetPlatform . Win64 ) ? "\\x64" : "" ;
Environment . SetEnvironmentVariable ( "PATH" , Utils . ResolveEnvironmentVariable ( WindowsSDKDir + "bin" + ConfigSuffix + ";%PATH%" ) ) ;
Environment . SetEnvironmentVariable ( "LIB" , Utils . ResolveEnvironmentVariable ( WindowsSDKDir + "lib" + ConfigSuffix + ";%LIB%" ) ) ;
Environment . SetEnvironmentVariable ( "INCLUDE" , Utils . ResolveEnvironmentVariable ( WindowsSDKDir + "include;%INCLUDE%" ) ) ;
}
}
/// <returns>The path to Windows SDK directory for the specified version.</returns>
2015-01-28 08:26:51 -05:00
private static string FindWindowsSDKInstallationFolder ( CPPTargetPlatform InPlatform )
2014-08-26 06:55:48 -04:00
{
// When targeting Windows XP on Visual Studio 2012+, we need to point at the older Windows SDK 7.1A that comes
// installed with Visual Studio 2012 Update 1. (http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx)
string Version ;
2015-01-28 08:26:51 -05:00
if ( WindowsPlatform . IsWindowsXPSupported ( ) )
2014-08-26 06:55:48 -04:00
{
Version = "v7.1A" ;
}
else switch ( WindowsPlatform . Compiler )
{
2015-05-05 15:32:10 -04:00
case WindowsCompiler . VisualStudio2015 :
if ( WindowsPlatform . bUseWindowsSDK10 )
{
Version = "v10.0" ;
}
else
{
Version = "v8.1" ;
}
break ;
2014-08-26 06:55:48 -04:00
case WindowsCompiler . VisualStudio2013 :
Version = "v8.1" ;
break ;
case WindowsCompiler . VisualStudio2012 :
Version = "v8.0" ;
break ;
default :
throw new BuildException ( "Unexpected compiler setting when trying to determine Windows SDK folder" ) ;
}
// Based on VCVarsQueryRegistry
2015-05-05 15:32:10 -04:00
string FinalResult = null ;
foreach ( string IndividualVersion in Version . Split ( '|' ) )
{
var Result = Microsoft . Win32 . Registry . GetValue ( @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Microsoft SDKs\Windows\" + IndividualVersion , "InstallationFolder" , null )
? ? Microsoft . Win32 . Registry . GetValue ( @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\" + IndividualVersion , "InstallationFolder" , null )
? ? Microsoft . Win32 . Registry . GetValue ( @"HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\" + IndividualVersion , "InstallationFolder" , null ) ;
2014-08-26 06:55:48 -04:00
2015-05-05 15:32:10 -04:00
if ( Result ! = null )
{
FinalResult = ( string ) Result ;
break ;
}
}
if ( FinalResult = = null )
2014-08-26 06:55:48 -04:00
{
throw new BuildException ( "Windows SDK {0} must be installed in order to build this target." , Version ) ;
}
2015-05-05 15:32:10 -04:00
return FinalResult ;
}
private static string FindNetFxSDKExtensionInstallationFolder ( )
{
string Version ;
switch ( WindowsPlatform . Compiler )
{
case WindowsCompiler . VisualStudio2015 :
if ( WindowsPlatform . bUseWindowsSDK10 )
{
Version = "4.6" ;
}
else
{
return string . Empty ;
}
break ;
default :
return string . Empty ;
}
string FinalResult = string . Empty ;
var Result = Microsoft . Win32 . Registry . GetValue ( @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\NETFXSDK\" + Version , "KitsInstallationFolder" , null )
? ? Microsoft . Win32 . Registry . GetValue ( @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\NETFXSDK\" + Version , "KitsInstallationFolder" , null ) ;
if ( Result ! = null )
{
FinalResult = ( ( string ) Result ) . TrimEnd ( '\\' ) ;
}
return FinalResult ;
}
private static string FindWindowsSDKExtensionInstallationFolder ( )
{
string Version ;
switch ( WindowsPlatform . Compiler )
{
case WindowsCompiler . VisualStudio2015 :
if ( WindowsPlatform . bUseWindowsSDK10 )
{
Version = "v10.0" ;
}
else
{
return string . Empty ;
}
break ;
default :
return string . Empty ;
}
// Based on VCVarsQueryRegistry
string FinalResult = null ;
{
var Result = Microsoft . Win32 . Registry . GetValue ( @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows SDKs\" + Version , "InstallationFolder" , null )
? ? Microsoft . Win32 . Registry . GetValue ( @"HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows SDKs\" + Version , "InstallationFolder" , null ) ;
if ( Result = = null )
{
Result = Microsoft . Win32 . Registry . GetValue ( @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\" + Version , "InstallationFolder" , null )
? ? Microsoft . Win32 . Registry . GetValue ( @"HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\" + Version , "InstallationFolder" , null ) ;
}
if ( Result ! = null )
{
FinalResult = ( ( string ) Result ) . TrimEnd ( '\\' ) ;
}
}
if ( FinalResult = = null )
{
FinalResult = string . Empty ;
}
return FinalResult ;
}
static Version FindWindowsSDKExtensionLatestVersion ( string WindowsSDKExtensionDir )
{
Version LatestVersion = new Version ( 0 , 0 , 0 , 0 ) ;
if ( WindowsPlatform . Compiler = = WindowsCompiler . VisualStudio2015 & &
WindowsPlatform . bUseWindowsSDK10 & &
! string . IsNullOrEmpty ( WindowsSDKExtensionDir ) & &
Directory . Exists ( WindowsSDKExtensionDir ) )
{
string IncludesBaseDirectory = Path . Combine ( WindowsSDKExtensionDir , "include" ) ;
if ( Directory . Exists ( IncludesBaseDirectory ) )
{
string [ ] IncludeVersions = Directory . GetDirectories ( IncludesBaseDirectory ) ;
foreach ( string IncludeVersion in IncludeVersions )
{
string VersionString = Path . GetFileName ( IncludeVersion ) ;
Version FoundVersion ;
if ( Version . TryParse ( VersionString , out FoundVersion ) & & FoundVersion > LatestVersion )
{
LatestVersion = FoundVersion ;
}
}
}
}
return LatestVersion ;
2014-08-26 06:55:48 -04:00
}
2015-08-05 15:54:57 -04:00
/** Gets the path to the 32bit tool binaries. */
static string GetVSToolPath32Bit ( string BaseVSToolPath )
2014-08-26 06:55:48 -04:00
{
2015-08-05 15:54:57 -04:00
return Path . Combine ( BaseVSToolPath , "../../VC/bin" ) ;
}
2014-08-26 06:55:48 -04:00
2015-08-05 15:54:57 -04:00
/** Gets the path to the 64bit tool binaries. */
static string GetVSToolPath64Bit ( string BaseVSToolPath )
{
// Use the native 64-bit compiler if present, otherwise use the amd64-on-x86 compiler. VS2012 Express only includes the latter.
var Result = Path . Combine ( BaseVSToolPath , "../../VC/bin/amd64" ) ;
if ( Directory . Exists ( Result ) )
{
return Result ;
2014-08-26 06:55:48 -04:00
}
2015-08-05 15:54:57 -04:00
return Path . Combine ( BaseVSToolPath , "../../VC/bin/x86_amd64" ) ;
2014-08-26 06:55:48 -04:00
}
/** Gets the path to the compiler. */
static string GetCompilerToolPath ( string PlatformVSToolPath )
{
// If we were asked to use Clang, then we'll redirect the path to the compiler to the LLVM installation directory
if ( WindowsPlatform . bCompileWithClang )
{
2014-12-18 16:52:27 -05:00
string Result ;
if ( WindowsPlatform . bUseVCCompilerArgs )
{
// Use regular Clang compiler on Windows
Result = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFilesX86 ) , "LLVM" , "msbuild-bin" , "cl.exe" ) ;
}
else
{
// Use 'clang-cl', a wrapper around Clang that supports Visual C++ compiler command-line arguments
Result = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFilesX86 ) , "LLVM" , "bin" , "clang.exe" ) ;
}
2014-08-26 06:55:48 -04:00
if ( ! File . Exists ( Result ) )
{
throw new BuildException ( "Clang was selected as the Windows compiler, but LLVM/Clang does not appear to be installed. Could not find: " + Result ) ;
}
return Result ;
}
return Path . Combine ( PlatformVSToolPath , "cl.exe" ) ;
}
/// <returns>The version of the compiler.</returns>
private static Version FindCLExeVersion ( string CompilerExe )
{
var ExeVersionInfo = FileVersionInfo . GetVersionInfo ( CompilerExe ) ;
if ( ExeVersionInfo = = null )
{
throw new BuildException ( "Failed to read the version number of: " + CompilerExe ) ;
}
return new Version ( ExeVersionInfo . FileMajorPart , ExeVersionInfo . FileMinorPart , ExeVersionInfo . FileBuildPart , ExeVersionInfo . FilePrivatePart ) ;
}
/** Gets the path to the linker. */
static string GetLinkerToolPath ( string PlatformVSToolPath )
{
// If we were asked to use Clang, then we'll redirect the path to the compiler to the LLVM installation directory
if ( WindowsPlatform . bCompileWithClang & & WindowsPlatform . bAllowClangLinker )
{
var Result = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFilesX86 ) , "LLVM" , "bin" , "lld.exe" ) ;
if ( ! File . Exists ( Result ) )
{
throw new BuildException ( "Clang was selected as the Windows compiler, but LLVM/Clang does not appear to be installed. Could not find: " + Result ) ;
}
return Result ;
}
return Path . Combine ( PlatformVSToolPath , "link.exe" ) ;
}
/** Gets the path to the library linker. */
static string GetLibraryLinkerToolPath ( string PlatformVSToolPath )
{
// Regardless of the target, if we're linking on a 64 bit machine, we want to use the 64 bit linker (it's faster than the 32 bit linker)
//@todo.WIN32: Using the 64-bit linker appears to be broken at the moment.
return Path . Combine ( PlatformVSToolPath , "lib.exe" ) ;
}
/** Gets the path to the resource compiler's rc.exe for the specified platform. */
2015-05-05 15:32:10 -04:00
string GetResourceCompilerToolPath ( CPPTargetPlatform Platform )
2014-08-26 06:55:48 -04:00
{
// 64 bit -- we can use the 32 bit version to target 64 bit on 32 bit OS.
2015-05-12 12:00:23 -04:00
if ( Platform = = CPPTargetPlatform . Win64 | | Platform = = CPPTargetPlatform . UWP )
2014-08-26 06:55:48 -04:00
{
2015-05-05 15:32:10 -04:00
if ( WindowsPlatform . Compiler = = WindowsCompiler . VisualStudio2015 & & WindowsPlatform . bUseWindowsSDK10 )
{
return Path . Combine ( WindowsSDKExtensionDir , "bin/x64/rc.exe" ) ;
}
else
{
return Path . Combine ( WindowsSDKDir , "bin/x64/rc.exe" ) ;
}
2014-08-26 06:55:48 -04:00
}
2015-05-12 12:00:23 -04:00
// @todo UWP: Verify that Windows XP will compile using VS 2015 (it should be supported)
2015-01-28 08:26:51 -05:00
if ( ! WindowsPlatform . IsWindowsXPSupported ( ) ) // Windows XP requires use to force Windows SDK 7.1 even on the newer compiler, so we need the old path RC.exe
2014-08-26 06:55:48 -04:00
{
2015-05-05 15:32:10 -04:00
if ( WindowsPlatform . Compiler = = WindowsCompiler . VisualStudio2015 & & WindowsPlatform . bUseWindowsSDK10 )
{
return Path . Combine ( WindowsSDKExtensionDir , "bin/x86/rc.exe" ) ;
}
else
{
return Path . Combine ( WindowsSDKDir , "bin/x86/rc.exe" ) ;
}
2014-08-26 06:55:48 -04:00
}
return Path . Combine ( WindowsSDKDir , "bin/rc.exe" ) ;
}
/** Gets the path to MSBuild. */
static string GetMSBuildToolPath ( )
{
string FrameworkDirectory = Environment . GetEnvironmentVariable ( "FrameworkDir" ) ;
string FrameworkVersion = Environment . GetEnvironmentVariable ( "FrameworkVersion" ) ;
if ( FrameworkDirectory = = null | | FrameworkVersion = = null )
{
throw new BuildException ( "NOTE: Please ensure that 64bit Tools are installed with DevStudio - there is usually an option to install these during install" ) ;
}
return Path . Combine ( FrameworkDirectory , FrameworkVersion , "MSBuild.exe" ) ;
}
static VCEnvironment EnvVars = null ;
}
}