2018-04-26 14:11:04 -04:00
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using System.IO ;
2015-08-12 12:13:26 -04:00
using System.Linq ;
2016-10-26 14:33:35 -04:00
using System.Xml.Linq ;
2017-08-31 12:08:38 -04:00
using Tools.DotNETCommon ;
2014-03-14 14:13:41 -04:00
namespace UnrealBuildTool
{
/// <summary>
/// Represents a folder within the master project (e.g. Visual Studio solution)
/// </summary>
2017-01-30 16:52:08 -05:00
class VisualStudioSolutionFolder : MasterProjectFolder
2014-03-14 14:13:41 -04:00
{
/// <summary>
/// Constructor
/// </summary>
2015-09-24 12:37:21 -04:00
public VisualStudioSolutionFolder ( ProjectFileGenerator InitOwnerProjectFileGenerator , string InitFolderName )
: base ( InitOwnerProjectFileGenerator , InitFolderName )
2014-03-14 14:13:41 -04:00
{
// Generate a unique GUID for this folder
// NOTE: When saving generated project files, we ignore differences in GUIDs if every other part of the file
// matches identically with the pre-existing file
FolderGUID = Guid . NewGuid ( ) ;
}
/// GUID for this folder
public Guid FolderGUID
{
get ;
private set ;
}
}
2017-01-30 16:52:08 -05:00
enum VCProjectFileFormat
2016-10-26 14:33:35 -04:00
{
Default , // Default to the best installed version, but allow SDKs to override
VisualStudio2012 , // Unsupported
2017-04-10 11:00:33 -04:00
VisualStudio2013 , // Unsupported
2016-10-26 14:33:35 -04:00
VisualStudio2015 ,
2016-11-23 15:34:07 -05:00
VisualStudio2017 ,
2016-10-26 14:33:35 -04:00
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Visual C++ project file generator implementation
/// </summary>
2017-01-30 16:52:08 -05:00
class VCProjectFileGenerator : ProjectFileGenerator
2014-03-14 14:13:41 -04:00
{
2016-10-26 14:33:35 -04:00
/// <summary>
2017-01-30 16:52:08 -05:00
/// The version of Visual Studio to generate project files for.
2016-10-26 14:33:35 -04:00
/// </summary>
2017-01-30 16:52:08 -05:00
[XmlConfigFile(Name = "Version")]
2018-05-23 21:04:31 -04:00
protected VCProjectFileFormat ProjectFileFormat = VCProjectFileFormat . Default ;
/// <summary>
/// Whether to write a solution option (suo) file for the sln
/// </summary>
[XmlConfigFile(Category = "BuildConfiguration")]
protected bool bWriteSolutionOptionFile = true ;
2017-01-30 16:52:08 -05:00
/// <summary>
/// Whether to add the -FastPDB option to build command lines by default
/// </summary>
[XmlConfigFile(Category = "BuildConfiguration")]
bool bAddFastPDBToProjects = false ;
/// <summary>
/// Whether to generate per-file intellisense data
/// </summary>
[XmlConfigFile(Category = "BuildConfiguration")]
bool bUsePerFileIntellisense = false ;
/// <summary>
/// Whether to include a dependency on ShaderCompileWorker when generating project files for the editor.
/// </summary>
[XmlConfigFile(Category = "BuildConfiguration")]
bool bEditorDependsOnShaderCompileWorker = true ;
/// <summary>
/// Override for the build tool to use in generated projects. If the compiler version is specified on the command line, we use the same argument on the
/// command line for generated projects.
/// </summary>
string BuildToolOverride ;
2016-10-26 14:33:35 -04:00
2017-09-25 14:08:25 -04:00
/// <summary>
2015-09-17 09:15:44 -04:00
/// Default constructor
2017-09-25 14:08:25 -04:00
/// </summary>
/// <param name="InOnlyGameProject">The single project to generate project files for, or null</param>
/// <param name="InProjectFileFormat">Override the project file format to use</param>
2018-10-01 08:42:30 -04:00
/// <param name="InArguments">Additional command line arguments</param>
public VCProjectFileGenerator ( FileReference InOnlyGameProject , VCProjectFileFormat InProjectFileFormat , CommandLineArguments InArguments )
2015-09-24 12:37:21 -04:00
: base ( InOnlyGameProject )
2015-09-17 09:15:44 -04:00
{
2017-01-30 16:52:08 -05:00
XmlConfig . ApplyTo ( this ) ;
2017-09-25 14:08:25 -04:00
if ( InProjectFileFormat ! = VCProjectFileFormat . Default )
2017-01-30 16:52:08 -05:00
{
2017-09-25 14:08:25 -04:00
ProjectFileFormat = InProjectFileFormat ;
}
2018-10-01 08:42:30 -04:00
if ( InArguments . HasOption ( "-2015" ) )
2017-09-25 14:08:25 -04:00
{
BuildToolOverride = "-2015" ;
}
2018-10-01 08:42:30 -04:00
else if ( InArguments . HasOption ( "-2017" ) )
2017-09-25 14:08:25 -04:00
{
BuildToolOverride = "-2017" ;
2017-01-30 16:52:08 -05:00
}
2015-09-17 09:15:44 -04:00
}
2014-03-14 14:13:41 -04:00
/// File extension for project files we'll be generating (e.g. ".vcxproj")
override public string ProjectFileExtension
{
get
{
return ".vcxproj" ;
}
}
/// <summary>
/// </summary>
2015-09-03 08:47:24 -04:00
public override void CleanProjectFiles ( DirectoryReference InMasterProjectDirectory , string InMasterProjectName , DirectoryReference InIntermediateProjectFilesDirectory )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
FileReference MasterProjectFile = FileReference . Combine ( InMasterProjectDirectory , InMasterProjectName ) ;
FileReference MasterProjDeleteFilename = MasterProjectFile + ".sln" ;
2017-01-30 16:52:08 -05:00
if ( FileReference . Exists ( MasterProjDeleteFilename ) )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
FileReference . Delete ( MasterProjDeleteFilename ) ;
2014-03-14 14:13:41 -04:00
}
MasterProjDeleteFilename = MasterProjectFile + ".sdf" ;
2017-01-30 16:52:08 -05:00
if ( FileReference . Exists ( MasterProjDeleteFilename ) )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
FileReference . Delete ( MasterProjDeleteFilename ) ;
2014-03-14 14:13:41 -04:00
}
MasterProjDeleteFilename = MasterProjectFile + ".suo" ;
2017-01-30 16:52:08 -05:00
if ( FileReference . Exists ( MasterProjDeleteFilename ) )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
FileReference . Delete ( MasterProjDeleteFilename ) ;
2014-03-14 14:13:41 -04:00
}
MasterProjDeleteFilename = MasterProjectFile + ".v11.suo" ;
2017-01-30 16:52:08 -05:00
if ( FileReference . Exists ( MasterProjDeleteFilename ) )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
FileReference . Delete ( MasterProjDeleteFilename ) ;
2014-03-14 14:13:41 -04:00
}
2015-05-05 15:32:10 -04:00
MasterProjDeleteFilename = MasterProjectFile + ".v12.suo" ;
2017-01-30 16:52:08 -05:00
if ( FileReference . Exists ( MasterProjDeleteFilename ) )
2015-05-05 15:32:10 -04:00
{
2017-01-30 16:52:08 -05:00
FileReference . Delete ( MasterProjDeleteFilename ) ;
2015-05-05 15:32:10 -04:00
}
2014-03-14 14:13:41 -04:00
// Delete the project files folder
2017-01-30 16:52:08 -05:00
if ( DirectoryReference . Exists ( InIntermediateProjectFilesDirectory ) )
2014-03-14 14:13:41 -04:00
{
try
{
2017-01-30 16:52:08 -05:00
DirectoryReference . Delete ( InIntermediateProjectFilesDirectory , true ) ;
2014-03-14 14:13:41 -04:00
}
catch ( Exception Ex )
{
2015-09-03 08:47:24 -04:00
Log . TraceInformation ( "Error while trying to clean project files path {0}. Ignored." , InIntermediateProjectFilesDirectory ) ;
2014-03-14 14:13:41 -04:00
Log . TraceInformation ( "\t" + Ex . Message ) ;
}
}
}
/// <summary>
/// Allocates a generator-specific project file object
/// </summary>
/// <param name="InitFilePath">Path to the project file</param>
/// <returns>The newly allocated project file object</returns>
2015-09-24 12:37:21 -04:00
protected override ProjectFile AllocateProjectFile ( FileReference InitFilePath )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
return new VCProjectFile ( InitFilePath , OnlyGameProject , ProjectFileFormat , bAddFastPDBToProjects , bUsePerFileIntellisense , BuildToolOverride ) ;
2014-03-14 14:13:41 -04:00
}
/// ProjectFileGenerator interface
2015-09-24 12:37:21 -04:00
public override MasterProjectFolder AllocateMasterProjectFolder ( ProjectFileGenerator InitOwnerProjectFileGenerator , string InitFolderName )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
return new VisualStudioSolutionFolder ( InitOwnerProjectFileGenerator , InitFolderName ) ;
2014-03-14 14:13:41 -04:00
}
2015-05-05 15:32:10 -04:00
/// "4.0", "12.0", or "14.0", etc...
2016-10-26 14:33:35 -04:00
static public string GetProjectFileToolVersionString ( VCProjectFileFormat ProjectFileFormat )
2015-05-05 15:32:10 -04:00
{
2016-10-26 14:33:35 -04:00
switch ( ProjectFileFormat )
{
case VCProjectFileFormat . VisualStudio2012 :
return "4.0" ;
case VCProjectFileFormat . VisualStudio2013 :
return "12.0" ;
case VCProjectFileFormat . VisualStudio2015 :
return "14.0" ;
case VCProjectFileFormat . VisualStudio2017 :
return "15.0" ;
2015-05-05 15:32:10 -04:00
}
2016-10-26 14:33:35 -04:00
return string . Empty ;
2015-05-05 15:32:10 -04:00
}
/// for instance: <PlatformToolset>v110</PlatformToolset>
2016-10-26 14:33:35 -04:00
static public string GetProjectFilePlatformToolsetVersionString ( VCProjectFileFormat ProjectFileFormat )
2015-05-05 15:32:10 -04:00
{
2016-10-26 14:33:35 -04:00
switch ( ProjectFileFormat )
{
case VCProjectFileFormat . VisualStudio2012 :
return "v110" ;
case VCProjectFileFormat . VisualStudio2013 :
return "v120" ;
case VCProjectFileFormat . VisualStudio2015 :
return "v140" ;
case VCProjectFileFormat . VisualStudio2017 :
return "v141" ;
}
return string . Empty ;
2014-03-14 14:13:41 -04:00
}
/// This is the platform name that Visual Studio is always guaranteed to support. We'll use this as
/// a platform for any project configurations where our actual platform is not supported by the
/// installed version of Visual Studio (e.g, "iOS")
2016-10-26 14:33:35 -04:00
public const string DefaultPlatformName = "Win32" ;
2014-03-14 14:13:41 -04:00
/// The platform name that must be used for .NET projects
2017-06-15 12:43:54 -04:00
public const string DotNetPlatformName = "Any CPU" ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// Configures project generator based on command-line options
/// </summary>
/// <param name="Arguments">Arguments passed into the program</param>
/// <param name="IncludeAllPlatforms">True if all platforms should be included</param>
2015-09-24 12:37:21 -04:00
protected override void ConfigureProjectFileGeneration ( String [ ] Arguments , ref bool IncludeAllPlatforms )
2014-03-14 14:13:41 -04:00
{
// Call parent implementation first
2015-09-24 12:37:21 -04:00
base . ConfigureProjectFileGeneration ( Arguments , ref IncludeAllPlatforms ) ;
2014-03-14 14:13:41 -04:00
}
2016-03-11 09:55:03 -05:00
/// <summary>
/// Adds Extra files that are specific to Visual Studio projects
/// </summary>
/// <param name="EngineProject">Project to add files to</param>
protected override void AddEngineExtrasFiles ( ProjectFile EngineProject )
{
base . AddEngineExtrasFiles ( EngineProject ) ;
// Add our UE4.natvis file
2017-01-30 16:52:08 -05:00
FileReference NatvisFilePath = FileReference . Combine ( UnrealBuildTool . EngineDirectory , "Extras" , "VisualStudioDebugging" , "UE4.natvis" ) ;
if ( FileReference . Exists ( NatvisFilePath ) )
2016-03-11 09:55:03 -05:00
{
EngineProject . AddFileToProject ( NatvisFilePath , UnrealBuildTool . EngineDirectory ) ;
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Selects which platforms and build configurations we want in the project file
/// </summary>
/// <param name="IncludeAllPlatforms">True if we should include ALL platforms that are supported on this machine. Otherwise, only desktop platforms will be included.</param>
/// <param name="SupportedPlatformNames">Output string for supported platforms, returned as comma-separated values.</param>
2015-09-24 12:37:21 -04:00
protected override void SetupSupportedPlatformsAndConfigurations ( bool IncludeAllPlatforms , out string SupportedPlatformNames )
2014-03-14 14:13:41 -04:00
{
// Call parent implementation to figure out the actual platforms
2015-09-24 12:37:21 -04:00
base . SetupSupportedPlatformsAndConfigurations ( IncludeAllPlatforms , out SupportedPlatformNames ) ;
2014-03-14 14:13:41 -04:00
2017-09-25 14:08:25 -04:00
// If we have a non-default setting for visual studio, check the compiler exists. If not, revert to the default.
if ( ProjectFileFormat = = VCProjectFileFormat . VisualStudio2015 )
2016-10-26 14:33:35 -04:00
{
2018-04-26 14:11:04 -04:00
if ( ! WindowsPlatform . HasCompiler ( WindowsCompiler . VisualStudio2015 ) )
2016-10-26 14:33:35 -04:00
{
2017-09-25 14:08:25 -04:00
Log . TraceWarning ( "Visual Studio C++ 2015 installation not found - ignoring preferred project file format." ) ;
ProjectFileFormat = VCProjectFileFormat . Default ;
}
}
else if ( ProjectFileFormat = = VCProjectFileFormat . VisualStudio2017 )
{
2018-04-26 14:11:04 -04:00
if ( ! WindowsPlatform . HasCompiler ( WindowsCompiler . VisualStudio2017 ) )
2017-09-25 14:08:25 -04:00
{
Log . TraceWarning ( "Visual Studio C++ 2017 installation not found - ignoring preferred project file format." ) ;
ProjectFileFormat = VCProjectFileFormat . Default ;
2016-10-26 14:33:35 -04:00
}
}
2014-03-14 14:13:41 -04:00
// Certain platforms override the project file format because their debugger add-ins may not yet support the latest
// version of Visual Studio. This is their chance to override that.
2016-03-07 20:55:29 -05:00
// ...but only if the user didn't override this via the command-line.
2016-10-26 14:33:35 -04:00
if ( ProjectFileFormat = = VCProjectFileFormat . Default )
2014-03-14 14:13:41 -04:00
{
2016-10-26 14:33:35 -04:00
// Pick the best platform installed by default
2018-09-18 13:17:54 -04:00
if ( WindowsPlatform . HasCompiler ( WindowsCompiler . VisualStudio2017 ) & & WindowsPlatform . HasIDE ( WindowsCompiler . VisualStudio2017 ) )
2016-10-26 14:33:35 -04:00
{
ProjectFileFormat = VCProjectFileFormat . VisualStudio2017 ;
}
2018-09-18 13:17:54 -04:00
else if ( WindowsPlatform . HasCompiler ( WindowsCompiler . VisualStudio2015 ) & & WindowsPlatform . HasIDE ( WindowsCompiler . VisualStudio2015 ) )
2018-04-26 14:11:04 -04:00
{
ProjectFileFormat = VCProjectFileFormat . VisualStudio2015 ;
}
2016-10-26 14:33:35 -04:00
// Allow the SDKs to override
2016-03-08 09:00:48 -05:00
foreach ( UnrealTargetPlatform SupportedPlatform in SupportedPlatforms )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
UEBuildPlatform BuildPlatform = UEBuildPlatform . GetBuildPlatform ( SupportedPlatform , true ) ;
2016-03-07 20:55:29 -05:00
if ( BuildPlatform ! = null )
2015-09-24 12:37:21 -04:00
{
2016-03-07 20:55:29 -05:00
// Don't worry about platforms that we're missing SDKs for
if ( BuildPlatform . HasRequiredSDKsInstalled ( ) = = SDKStatus . Valid )
2014-03-14 14:13:41 -04:00
{
2018-05-29 13:30:06 -04:00
VCProjectFileFormat ProposedFormat = BuildPlatform . GetRequiredVisualStudioVersion ( ) ;
2015-09-29 13:17:32 -04:00
2018-05-29 13:30:06 -04:00
if ( ProposedFormat ! = VCProjectFileFormat . Default )
2015-09-29 13:17:32 -04:00
{
2018-05-29 13:30:06 -04:00
// Reduce the Visual Studio version to the max supported by each platform we plan to include.
if ( ProjectFileFormat = = VCProjectFileFormat . Default | | ProposedFormat < ProjectFileFormat )
{
ProjectFileFormat = ProposedFormat ;
}
2014-03-14 14:13:41 -04:00
}
}
2015-09-24 12:37:21 -04:00
}
2014-03-14 14:13:41 -04:00
}
}
}
/// <summary>
/// Used to sort VC solution config names along with the config and platform values
/// </summary>
class VCSolutionConfigCombination
{
/// Visual Studio solution configuration name for this config+platform
public string VCSolutionConfigAndPlatformName ;
/// Configuration name
public UnrealTargetConfiguration Configuration ;
/// Platform name
public UnrealTargetPlatform Platform ;
/// The target configuration name
public string TargetConfigurationName ;
public override string ToString ( )
{
return String . Format ( "{0}={1} {2} {3}" , VCSolutionConfigAndPlatformName , Configuration , Platform , TargetConfigurationName ) ;
}
}
/// <summary>
/// Composes a string to use for the Visual Studio solution configuration, given a build configuration and target rules configuration name
/// </summary>
/// <param name="Configuration">The build configuration</param>
/// <param name="TargetConfigurationName">The target rules configuration name</param>
/// <returns>The generated solution configuration name</returns>
2015-09-24 12:37:21 -04:00
string MakeSolutionConfigurationName ( UnrealTargetConfiguration Configuration , string TargetConfigurationName )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
string SolutionConfigName = Configuration . ToString ( ) ;
2014-03-14 14:13:41 -04:00
// Don't bother postfixing "Game" or "Program" -- that will be the default when using "Debug", "Development", etc.
// Also don't postfix "RocketGame" when we're building Rocket game projects. That's the only type of game there is in that case!
2017-01-30 16:52:08 -05:00
if ( ! TargetConfigurationName . Equals ( TargetType . Game . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) & &
! TargetConfigurationName . Equals ( TargetType . Program . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) )
2014-03-14 14:13:41 -04:00
{
SolutionConfigName + = " " + TargetConfigurationName ;
}
return SolutionConfigName ;
}
2016-10-26 14:33:35 -04:00
/// <summary>
/// Writes the project files to disk
/// </summary>
/// <returns>True if successful</returns>
2018-10-01 09:45:58 -04:00
protected override bool WriteProjectFiles ( PlatformProjectGeneratorCollection PlatformProjectGenerators )
2016-10-26 14:33:35 -04:00
{
2018-10-01 09:45:58 -04:00
if ( ! base . WriteProjectFiles ( PlatformProjectGenerators ) )
2016-10-26 14:33:35 -04:00
{
return false ;
}
// Write AutomationReferences file
if ( AutomationProjectFiles . Any ( ) )
{
XNamespace NS = XNamespace . Get ( "http://schemas.microsoft.com/developer/msbuild/2003" ) ;
DirectoryReference AutomationToolDir = DirectoryReference . Combine ( UnrealBuildTool . EngineSourceDirectory , "Programs" , "AutomationTool" ) ;
new XDocument (
new XElement ( NS + "Project" ,
new XAttribute ( "ToolsVersion" , VCProjectFileGenerator . GetProjectFileToolVersionString ( ProjectFileFormat ) ) ,
new XAttribute ( "DefaultTargets" , "Build" ) ,
new XElement ( NS + "ItemGroup" ,
from AutomationProject in AutomationProjectFiles
select new XElement ( NS + "ProjectReference" ,
new XAttribute ( "Include" , AutomationProject . ProjectFilePath . MakeRelativeTo ( AutomationToolDir ) ) ,
new XElement ( NS + "Project" , ( AutomationProject as VCSharpProjectFile ) . ProjectGUID . ToString ( "B" ) ) ,
new XElement ( NS + "Name" , AutomationProject . ProjectFilePath . GetFileNameWithoutExtension ( ) ) ,
new XElement ( NS + "Private" , "false" )
)
)
)
) . Save ( FileReference . Combine ( AutomationToolDir , "AutomationTool.csproj.References" ) . FullName ) ;
}
return true ;
}
2014-03-14 14:13:41 -04:00
2018-10-01 09:45:58 -04:00
protected override bool WriteMasterProjectFile ( ProjectFile UBTProject , PlatformProjectGeneratorCollection PlatformProjectGenerators )
2014-03-14 14:13:41 -04:00
{
bool bSuccess = true ;
2016-03-08 09:00:48 -05:00
string SolutionFileName = MasterProjectName + ".sln" ;
2014-03-14 14:13:41 -04:00
// Setup solution file content
2016-03-08 09:00:48 -05:00
StringBuilder VCSolutionFileContent = new StringBuilder ( ) ;
2014-03-14 14:13:41 -04:00
2015-03-09 06:21:00 -04:00
const string VersionTag = "# UnrealEngineGeneratedSolutionVersion=1.0" ;
2014-03-14 14:13:41 -04:00
// Solution file header
2016-09-15 08:33:13 -04:00
if ( ProjectFileFormat = = VCProjectFileFormat . VisualStudio2017 )
{
VCSolutionFileContent . Append (
ProjectFileGenerator . NewLine +
"Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator . NewLine +
"# Visual Studio 15" + ProjectFileGenerator . NewLine +
2016-10-26 14:33:35 -04:00
"VisualStudioVersion = 15.0.25807.0" + ProjectFileGenerator . NewLine +
2016-09-15 08:33:13 -04:00
"MinimumVisualStudioVersion = 10.0.40219.1" + ProjectFileGenerator . NewLine ) ;
}
else if ( ProjectFileFormat = = VCProjectFileFormat . VisualStudio2015 )
2015-05-05 15:32:10 -04:00
{
VCSolutionFileContent . Append (
ProjectFileGenerator . NewLine +
"Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator . NewLine +
"# Visual Studio 14" + ProjectFileGenerator . NewLine +
"VisualStudioVersion = 14.0.22310.1" + ProjectFileGenerator . NewLine +
"MinimumVisualStudioVersion = 10.0.40219.1" + ProjectFileGenerator . NewLine ) ;
}
2015-09-24 12:37:21 -04:00
else if ( ProjectFileFormat = = VCProjectFileFormat . VisualStudio2013 )
2014-03-14 14:13:41 -04:00
{
VCSolutionFileContent . Append (
ProjectFileGenerator . NewLine +
"Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator . NewLine +
2015-03-09 06:21:00 -04:00
"# Visual Studio 2013" + ProjectFileGenerator . NewLine +
VersionTag + ProjectFileGenerator . NewLine ) ;
2016-03-17 20:30:20 -04:00
}
else if ( ProjectFileFormat = = VCProjectFileFormat . VisualStudio2012 )
{
VCSolutionFileContent . Append (
ProjectFileGenerator . NewLine +
"Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator . NewLine +
"# Visual Studio 2012" + ProjectFileGenerator . NewLine +
VersionTag + ProjectFileGenerator . NewLine ) ;
}
2014-03-14 14:13:41 -04:00
else
{
2015-09-24 12:37:21 -04:00
throw new BuildException ( "Unexpected ProjectFileFormat" ) ;
2014-03-14 14:13:41 -04:00
}
2016-02-03 15:40:40 -05:00
// Find the projects for ShaderCompileWorker and UnrealLightmass
2015-08-12 12:13:26 -04:00
ProjectFile ShaderCompileWorkerProject = null ;
2016-02-03 15:40:40 -05:00
ProjectFile UnrealLightmassProject = null ;
2015-09-24 12:37:21 -04:00
foreach ( ProjectFile Project in AllProjectFiles )
2015-08-12 12:13:26 -04:00
{
2015-09-24 12:37:21 -04:00
if ( Project . ProjectTargets . Count = = 1 )
2015-08-12 12:13:26 -04:00
{
2015-09-03 08:47:24 -04:00
FileReference TargetFilePath = Project . ProjectTargets [ 0 ] . TargetFilePath ;
2016-02-03 15:40:40 -05:00
if ( TargetFilePath ! = null )
{
string TargetFileName = TargetFilePath . GetFileNameWithoutAnyExtensions ( ) ;
if ( TargetFileName . Equals ( "ShaderCompileWorker" , StringComparison . InvariantCultureIgnoreCase ) )
{
ShaderCompileWorkerProject = Project ;
}
else if ( TargetFileName . Equals ( "UnrealLightmass" , StringComparison . InvariantCultureIgnoreCase ) )
{
UnrealLightmassProject = Project ;
}
}
if ( ShaderCompileWorkerProject ! = null
& & UnrealLightmassProject ! = null )
2015-08-12 12:13:26 -04:00
{
break ;
}
}
}
2014-03-14 14:13:41 -04:00
// Solution folders, files and project entries
{
// This the GUID that Visual Studio uses to identify a solution folder
2016-03-08 09:00:48 -05:00
string SolutionFolderEntryGUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}" ;
2014-03-14 14:13:41 -04:00
// Solution folders
{
2016-03-08 09:00:48 -05:00
List < MasterProjectFolder > AllSolutionFolders = new List < MasterProjectFolder > ( ) ;
2015-09-24 12:37:21 -04:00
System . Action < List < MasterProjectFolder > /* Folders */ > GatherFoldersFunction = null ;
2014-03-14 14:13:41 -04:00
GatherFoldersFunction = FolderList = >
{
2015-09-24 12:37:21 -04:00
AllSolutionFolders . AddRange ( FolderList ) ;
2016-03-08 09:00:48 -05:00
foreach ( MasterProjectFolder CurSubFolder in FolderList )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
GatherFoldersFunction ( CurSubFolder . SubFolders ) ;
2014-03-14 14:13:41 -04:00
}
} ;
2015-09-24 12:37:21 -04:00
GatherFoldersFunction ( RootFolder . SubFolders ) ;
2014-03-14 14:13:41 -04:00
2015-09-24 12:37:21 -04:00
foreach ( VisualStudioSolutionFolder CurFolder in AllSolutionFolders )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
string FolderGUIDString = CurFolder . FolderGUID . ToString ( "B" ) . ToUpperInvariant ( ) ;
2014-03-14 14:13:41 -04:00
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
"Project(\"" + SolutionFolderEntryGUID + "\") = \"" + CurFolder . FolderName + "\", \"" + CurFolder . FolderName + "\", \"" + FolderGUIDString + "\"" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
// Add any files that are inlined right inside the solution folder
2015-09-24 12:37:21 -04:00
if ( CurFolder . Files . Count > 0 )
2014-03-14 14:13:41 -04:00
{
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" ProjectSection(SolutionItems) = preProject" + ProjectFileGenerator . NewLine ) ;
2016-03-08 09:00:48 -05:00
foreach ( string CurFile in CurFolder . Files )
2014-03-14 14:13:41 -04:00
{
// Syntax is: <relative file path> = <relative file path>
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" " + CurFile + " = " + CurFile + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" EndProjectSection" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
2015-09-24 12:37:21 -04:00
2014-03-14 14:13:41 -04:00
VCSolutionFileContent . Append (
"EndProject" + ProjectFileGenerator . NewLine
) ;
}
}
// Project files
2015-09-24 12:37:21 -04:00
foreach ( MSBuildProjectFile CurProject in AllProjectFiles )
2014-03-14 14:13:41 -04:00
{
// Visual Studio uses different GUID types depending on the project type
string ProjectTypeGUID = CurProject . ProjectTypeGUID ;
// NOTE: The project name in the solution doesn't actually *have* to match the project file name on disk. However,
// we prefer it when it does match so we use the actual file name here.
2016-03-08 09:00:48 -05:00
string ProjectNameInSolution = CurProject . ProjectFilePath . GetFileNameWithoutExtension ( ) ;
2014-03-14 14:13:41 -04:00
// Use the existing project's GUID that's already known to us
2016-03-08 09:00:48 -05:00
string ProjectGUID = CurProject . ProjectGUID . ToString ( "B" ) . ToUpperInvariant ( ) ;
2014-03-14 14:13:41 -04:00
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
"Project(\"" + ProjectTypeGUID + "\") = \"" + ProjectNameInSolution + "\", \"" + CurProject . ProjectFilePath . MakeRelativeTo ( ProjectFileGenerator . MasterProjectPath ) + "\", \"" + ProjectGUID + "\"" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
// Setup dependency on UnrealBuildTool, if we need that. This makes sure that UnrealBuildTool is
// freshly compiled before kicking off any build operations on this target project
2015-09-24 12:37:21 -04:00
if ( ! CurProject . IsStubProject )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
List < ProjectFile > Dependencies = new List < ProjectFile > ( ) ;
2015-03-26 10:53:06 -04:00
if ( CurProject . IsGeneratedProject & & UBTProject ! = null & & CurProject ! = UBTProject )
2014-03-14 14:13:41 -04:00
{
2015-03-26 10:53:06 -04:00
Dependencies . Add ( UBTProject ) ;
Dependencies . AddRange ( UBTProject . DependsOnProjects ) ;
}
2017-08-31 12:08:38 -04:00
if ( bEditorDependsOnShaderCompileWorker & & ! bUsePrecompiled & & CurProject . IsGeneratedProject & & ShaderCompileWorkerProject ! = null & & CurProject . ProjectTargets . Any ( x = > x . TargetRules ! = null & & x . TargetRules . Type = = TargetType . Editor ) )
2015-08-12 12:13:26 -04:00
{
Dependencies . Add ( ShaderCompileWorkerProject ) ;
}
2015-03-26 10:53:06 -04:00
Dependencies . AddRange ( CurProject . DependsOnProjects ) ;
2014-03-14 14:13:41 -04:00
2015-03-26 10:53:06 -04:00
if ( Dependencies . Count > 0 )
{
VCSolutionFileContent . Append ( "\tProjectSection(ProjectDependencies) = postProject" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
// Setup any addition dependencies this project has...
2016-03-08 09:00:48 -05:00
foreach ( ProjectFile DependsOnProject in Dependencies )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
string DependsOnProjectGUID = ( ( MSBuildProjectFile ) DependsOnProject ) . ProjectGUID . ToString ( "B" ) . ToUpperInvariant ( ) ;
2015-03-26 10:53:06 -04:00
VCSolutionFileContent . Append ( "\t\t" + DependsOnProjectGUID + " = " + DependsOnProjectGUID + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
2015-03-26 10:53:06 -04:00
VCSolutionFileContent . Append ( "\tEndProjectSection" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
}
VCSolutionFileContent . Append (
"EndProject" + ProjectFileGenerator . NewLine
) ;
}
}
// Solution configuration platforms. This is just a list of all of the platforms and configurations that
// appear in Visual Studio's build configuration selector.
2016-03-08 09:00:48 -05:00
List < VCSolutionConfigCombination > SolutionConfigCombinations = new List < VCSolutionConfigCombination > ( ) ;
2014-03-14 14:13:41 -04:00
// The "Global" section has source control, solution configurations, project configurations,
// preferences, and project hierarchy data
{
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
"Global" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
{
{
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" GlobalSection(SolutionConfigurationPlatforms) = preSolution" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
2016-03-08 09:00:48 -05:00
Dictionary < string , Tuple < UnrealTargetConfiguration , string > > SolutionConfigurationsValidForProjects = new Dictionary < string , Tuple < UnrealTargetConfiguration , string > > ( ) ;
HashSet < UnrealTargetPlatform > PlatformsValidForProjects = new HashSet < UnrealTargetPlatform > ( ) ;
2014-03-14 14:13:41 -04:00
2016-03-08 09:00:48 -05:00
foreach ( UnrealTargetConfiguration CurConfiguration in SupportedConfigurations )
2014-03-14 14:13:41 -04:00
{
2018-04-26 14:11:04 -04:00
if ( InstalledPlatformInfo . IsValidConfiguration ( CurConfiguration , EProjectType . Code ) )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
foreach ( UnrealTargetPlatform CurPlatform in SupportedPlatforms )
2014-03-14 14:13:41 -04:00
{
2018-04-26 14:11:04 -04:00
if ( InstalledPlatformInfo . IsValidPlatform ( CurPlatform , EProjectType . Code ) )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
foreach ( ProjectFile CurProject in AllProjectFiles )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( ! CurProject . IsStubProject )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( CurProject . ProjectTargets . Count = = 0 )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
throw new BuildException ( "Expecting project '" + CurProject . ProjectFilePath + "' to have at least one ProjectTarget associated with it!" ) ;
2014-03-14 14:13:41 -04:00
}
// Figure out the set of valid target configuration names
2016-03-08 09:00:48 -05:00
foreach ( ProjectTarget ProjectTarget in CurProject . ProjectTargets )
2014-03-14 14:13:41 -04:00
{
2018-10-01 09:45:58 -04:00
if ( VCProjectFile . IsValidProjectPlatformAndConfiguration ( ProjectTarget , CurPlatform , CurConfiguration , PlatformProjectGenerators ) )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
PlatformsValidForProjects . Add ( CurPlatform ) ;
2014-03-14 14:13:41 -04:00
// Default to a target configuration name of "Game", since that will collapse down to an empty string
2017-01-30 16:52:08 -05:00
string TargetConfigurationName = TargetType . Game . ToString ( ) ;
2015-09-24 12:37:21 -04:00
if ( ProjectTarget . TargetRules ! = null )
2014-03-14 14:13:41 -04:00
{
2016-12-13 11:58:16 -05:00
TargetConfigurationName = ProjectTarget . TargetRules . Type . ToString ( ) ;
2014-03-14 14:13:41 -04:00
}
2016-03-08 09:00:48 -05:00
string SolutionConfigName = MakeSolutionConfigurationName ( CurConfiguration , TargetConfigurationName ) ;
2015-09-24 12:37:21 -04:00
SolutionConfigurationsValidForProjects [ SolutionConfigName ] = new Tuple < UnrealTargetConfiguration , string > ( CurConfiguration , TargetConfigurationName ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
}
}
}
}
2016-03-08 09:00:48 -05:00
foreach ( UnrealTargetPlatform CurPlatform in PlatformsValidForProjects )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
foreach ( KeyValuePair < string , Tuple < UnrealTargetConfiguration , string > > SolutionConfigKeyValue in SolutionConfigurationsValidForProjects )
2014-03-14 14:13:41 -04:00
{
// e.g. "Development|Win64 = Development|Win64"
2016-03-08 09:00:48 -05:00
string SolutionConfigName = SolutionConfigKeyValue . Key ;
UnrealTargetConfiguration Configuration = SolutionConfigKeyValue . Value . Item1 ;
string TargetConfigurationName = SolutionConfigKeyValue . Value . Item2 ;
2014-03-14 14:13:41 -04:00
2016-03-08 09:00:48 -05:00
string SolutionPlatformName = CurPlatform . ToString ( ) ;
2014-03-14 14:13:41 -04:00
2016-03-08 09:00:48 -05:00
string SolutionConfigAndPlatformPair = SolutionConfigName + "|" + SolutionPlatformName ;
2014-03-14 14:13:41 -04:00
SolutionConfigCombinations . Add (
new VCSolutionConfigCombination
{
VCSolutionConfigAndPlatformName = SolutionConfigAndPlatformPair ,
Configuration = Configuration ,
Platform = CurPlatform ,
TargetConfigurationName = TargetConfigurationName
}
) ;
}
}
// Sort the list of solution platform strings alphabetically (Visual Studio prefers it)
SolutionConfigCombinations . Sort (
2015-09-24 12:37:21 -04:00
new Comparison < VCSolutionConfigCombination > (
( x , y ) = > { return String . Compare ( x . VCSolutionConfigAndPlatformName , y . VCSolutionConfigAndPlatformName , StringComparison . InvariantCultureIgnoreCase ) ; }
)
2014-03-14 14:13:41 -04:00
) ;
2016-03-08 09:00:48 -05:00
HashSet < string > AppendedSolutionConfigAndPlatformNames = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
foreach ( VCSolutionConfigCombination SolutionConfigCombination in SolutionConfigCombinations )
2014-03-14 14:13:41 -04:00
{
// We alias "Game" and "Program" to both have the same solution configuration, so we're careful not to add the same combination twice.
2015-09-24 12:37:21 -04:00
if ( ! AppendedSolutionConfigAndPlatformNames . Contains ( SolutionConfigCombination . VCSolutionConfigAndPlatformName ) )
2014-03-14 14:13:41 -04:00
{
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" " + SolutionConfigCombination . VCSolutionConfigAndPlatformName + " = " + SolutionConfigCombination . VCSolutionConfigAndPlatformName + ProjectFileGenerator . NewLine ) ;
AppendedSolutionConfigAndPlatformNames . Add ( SolutionConfigCombination . VCSolutionConfigAndPlatformName ) ;
2014-03-14 14:13:41 -04:00
}
}
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" EndGlobalSection" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
// Assign each project's "project configuration" to our "solution platform + configuration" pairs. This
// also sets up which projects are actually built when building the solution.
{
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" GlobalSection(ProjectConfigurationPlatforms) = postSolution" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
2016-03-08 09:00:48 -05:00
List < VCSolutionConfigCombination > CombinationsThatWereMatchedToProjects = new List < VCSolutionConfigCombination > ( ) ;
2014-03-14 14:13:41 -04:00
2015-09-24 12:37:21 -04:00
foreach ( MSBuildProjectFile CurProject in AllProjectFiles )
2014-03-14 14:13:41 -04:00
{
// NOTE: We don't emit solution configuration entries for "stub" projects. Those projects are only
// built using UnrealBuildTool and don't require a presence in the solution project list
// NOTE: We also process projects that were "imported" here, hoping to match those to our solution
// configurations. In some cases this may not be successful though. Imported projects
// should always be carefully setup to match our project generator's solution configs.
2015-09-24 12:37:21 -04:00
if ( ! CurProject . IsStubProject )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( CurProject . ProjectTargets . Count = = 0 )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
throw new BuildException ( "Expecting project '" + CurProject . ProjectFilePath + "' to have at least one ProjectTarget associated with it!" ) ;
2014-03-14 14:13:41 -04:00
}
2017-01-30 16:52:08 -05:00
bool IsProgramProject = CurProject . ProjectTargets [ 0 ] . TargetRules ! = null & & CurProject . ProjectTargets [ 0 ] . TargetRules . Type = = TargetType . Program ;
2014-03-14 14:13:41 -04:00
2016-03-08 09:00:48 -05:00
HashSet < string > GameOrProgramConfigsAlreadyMapped = new HashSet < string > ( ) ;
foreach ( VCSolutionConfigCombination SolutionConfigCombination in SolutionConfigCombinations )
2014-03-14 14:13:41 -04:00
{
// Handle aliasing of Program and Game target configuration names
2015-09-24 12:37:21 -04:00
if ( ( IsProgramProject & & GameOrProgramConfigsAlreadyMapped . Add ( SolutionConfigCombination . VCSolutionConfigAndPlatformName ) ) | |
2017-01-30 16:52:08 -05:00
IsProgramProject & & SolutionConfigCombination . TargetConfigurationName ! = TargetType . Game . ToString ( ) | |
! IsProgramProject & & SolutionConfigCombination . TargetConfigurationName ! = TargetType . Program . ToString ( ) )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
string TargetConfigurationName = SolutionConfigCombination . TargetConfigurationName ;
2016-08-04 10:41:07 -04:00
if ( IsProgramProject )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
TargetConfigurationName = TargetType . Program . ToString ( ) ;
2014-03-14 14:13:41 -04:00
}
// Now, we want to find a target in this project that maps to the current solution config combination. Only up to one target should
// and every solution config combination should map to at least one target in one project (otherwise we shouldn't have added it!).
ProjectTarget MatchingProjectTarget = null ;
2016-03-08 09:00:48 -05:00
foreach ( ProjectTarget ProjectTarget in CurProject . ProjectTargets )
2014-03-14 14:13:41 -04:00
{
2018-10-01 09:45:58 -04:00
bool IsMatchingCombination = VCProjectFile . IsValidProjectPlatformAndConfiguration ( ProjectTarget , SolutionConfigCombination . Platform , SolutionConfigCombination . Configuration , PlatformProjectGenerators ) ;
2015-09-24 12:37:21 -04:00
if ( ProjectTarget . TargetRules ! = null )
2014-03-14 14:13:41 -04:00
{
2016-12-13 11:58:16 -05:00
if ( TargetConfigurationName ! = ProjectTarget . TargetRules . Type . ToString ( ) )
2014-03-14 14:13:41 -04:00
{
// Solution configuration name for this combination doesn't match this target's configuration name. It's not buildable.
IsMatchingCombination = false ;
}
}
else
{
// UBT gets a pass because it is a dependency of every single configuration combination
2015-09-24 12:37:21 -04:00
if ( CurProject ! = UBTProject & &
2014-08-29 12:25:29 -04:00
! CurProject . ShouldBuildForAllSolutionTargets & &
2017-01-30 16:52:08 -05:00
TargetConfigurationName ! = TargetType . Game . ToString ( ) )
2014-03-14 14:13:41 -04:00
{
// Can't build non-generated project in configurations except for the default (Game)
IsMatchingCombination = false ;
}
}
2015-09-24 12:37:21 -04:00
if ( IsMatchingCombination )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( MatchingProjectTarget ! = null )
2014-03-14 14:13:41 -04:00
{
// Not expecting more than one target to match a single solution configuration per project!
2015-09-24 12:37:21 -04:00
throw new BuildException ( "Not expecting more than one target for project " + CurProject . ProjectFilePath + " to match solution configuration " + SolutionConfigCombination . VCSolutionConfigAndPlatformName ) ;
2014-03-14 14:13:41 -04:00
}
MatchingProjectTarget = ProjectTarget ;
// NOTE: For faster perf, we could "break" here and bail out early, but that would circumvent the error checking
// for multiple targets within a project that may map to a single solution configuration.
}
}
2016-03-08 09:00:48 -05:00
UnrealTargetConfiguration SolutionConfiguration = SolutionConfigCombination . Configuration ;
UnrealTargetPlatform SolutionPlatform = SolutionConfigCombination . Platform ;
2014-03-14 14:13:41 -04:00
2015-09-24 12:37:21 -04:00
if ( MatchingProjectTarget = = null )
2014-03-14 14:13:41 -04:00
{
// The current configuration/platform and target configuration name doesn't map to anything our project actually supports.
// We'll map it to a default config.
SolutionConfiguration = UnrealTargetConfiguration . Development ;
// Prefer using Win64 as the default, but fall back to a platform the project file actually supports if needed. This is for
// projects that can never be compiled in Windows, such as UnrealLaunchDaemon which is an iOS-only program
SolutionPlatform = UnrealTargetPlatform . Win64 ;
2015-09-24 12:37:21 -04:00
if ( CurProject . ProjectTargets [ 0 ] . TargetRules ! = null )
2014-03-14 14:13:41 -04:00
{
2016-12-13 11:58:16 -05:00
if ( ! CurProject . ProjectTargets [ 0 ] . SupportedPlatforms . Contains ( SolutionPlatform ) )
2014-03-14 14:13:41 -04:00
{
2016-12-13 11:58:16 -05:00
SolutionPlatform = CurProject . ProjectTargets [ 0 ] . SupportedPlatforms [ 0 ] ;
2014-03-14 14:13:41 -04:00
}
}
2015-09-24 12:37:21 -04:00
if ( IsProgramProject )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
TargetConfigurationName = TargetType . Program . ToString ( ) ;
2014-03-14 14:13:41 -04:00
}
2015-09-24 12:37:21 -04:00
else
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
TargetConfigurationName = TargetType . Game . ToString ( ) ;
2014-03-14 14:13:41 -04:00
}
}
// If the project wants to always build in "Development", regardless of what the solution
// configuration is set to, then we'll do that here. This is used for projects like
// UnrealBuildTool and ShaderCompileWorker
2015-09-24 12:37:21 -04:00
if ( MatchingProjectTarget ! = null )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( MatchingProjectTarget . ForceDevelopmentConfiguration )
2014-03-14 14:13:41 -04:00
{
SolutionConfiguration = UnrealTargetConfiguration . Development ;
}
}
2016-02-03 15:40:40 -05:00
// Always allow SCW and UnrealLighmass to build in editor configurations
2017-01-30 16:52:08 -05:00
if ( MatchingProjectTarget = = null & & SolutionConfigCombination . TargetConfigurationName = = TargetType . Editor . ToString ( ) & & SolutionConfigCombination . Platform = = UnrealTargetPlatform . Win64 )
2015-08-12 12:13:26 -04:00
{
2016-02-03 15:40:40 -05:00
if ( CurProject = = ShaderCompileWorkerProject )
2015-08-13 08:55:10 -04:00
{
MatchingProjectTarget = ShaderCompileWorkerProject . ProjectTargets [ 0 ] ;
}
2016-02-03 15:40:40 -05:00
else if ( CurProject = = UnrealLightmassProject )
{
MatchingProjectTarget = UnrealLightmassProject . ProjectTargets [ 0 ] ;
}
2015-08-12 12:13:26 -04:00
}
2014-03-14 14:13:41 -04:00
string ProjectConfigName ;
string ProjectPlatformName ;
2016-10-26 14:33:35 -04:00
if ( CurProject . IsStubProject )
{
if ( SolutionPlatform ! = UnrealTargetPlatform . Unknown | | SolutionConfiguration ! = UnrealTargetConfiguration . Unknown )
{
throw new BuildException ( "Stub project was expecting platform and configuration type to be set to Unknown" ) ;
}
ProjectPlatformName = MSBuildProjectFile . StubProjectPlatformName ;
ProjectConfigName = MSBuildProjectFile . StubProjectConfigurationName ;
}
else
{
2018-10-01 09:45:58 -04:00
CurProject . MakeProjectPlatformAndConfigurationNames ( SolutionPlatform , SolutionConfiguration , TargetConfigurationName , PlatformProjectGenerators , out ProjectPlatformName , out ProjectConfigName ) ;
2016-10-26 14:33:35 -04:00
}
2014-03-14 14:13:41 -04:00
2016-03-08 09:00:48 -05:00
string ProjectConfigAndPlatformPair = ProjectConfigName . ToString ( ) + "|" + ProjectPlatformName . ToString ( ) ;
2014-03-14 14:13:41 -04:00
// e.g. "{4232C52C-680F-4850-8855-DC39419B5E9B}.Debug|iOS.ActiveCfg = iOS_Debug|Win32"
2016-03-08 09:00:48 -05:00
string CurProjectGUID = CurProject . ProjectGUID . ToString ( "B" ) . ToUpperInvariant ( ) ;
2014-03-14 14:13:41 -04:00
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" " + CurProjectGUID + "." + SolutionConfigCombination . VCSolutionConfigAndPlatformName + ".ActiveCfg = " + ProjectConfigAndPlatformPair + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
// Set whether this project configuration should be built when the user initiates "build solution"
2015-09-24 12:37:21 -04:00
if ( MatchingProjectTarget ! = null & & CurProject . ShouldBuildByDefaultForSolutionTargets )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
// Some targets are "dummy targets"; they only exist to show user friendly errors in VS. Weed them out here, and don't set them to build by default.
List < UnrealTargetPlatform > SupportedPlatforms = null ;
if ( MatchingProjectTarget . TargetRules ! = null )
{
SupportedPlatforms = new List < UnrealTargetPlatform > ( ) ;
2016-12-13 11:58:16 -05:00
SupportedPlatforms . AddRange ( MatchingProjectTarget . SupportedPlatforms ) ;
2015-09-24 12:37:21 -04:00
}
if ( SupportedPlatforms = = null | | SupportedPlatforms . Contains ( SolutionConfigCombination . Platform ) )
{
VCSolutionFileContent . Append (
" " + CurProjectGUID + "." + SolutionConfigCombination . VCSolutionConfigAndPlatformName + ".Build.0 = " + ProjectConfigAndPlatformPair + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
2018-10-01 09:45:58 -04:00
PlatformProjectGenerator ProjGen = PlatformProjectGenerators . GetPlatformProjectGenerator ( SolutionConfigCombination . Platform , true ) ;
2015-09-24 12:37:21 -04:00
if ( MatchingProjectTarget . ProjectDeploys | |
( ( ProjGen ! = null ) & & ( ProjGen . GetVisualStudioDeploymentEnabled ( SolutionPlatform , SolutionConfiguration ) = = true ) ) )
{
VCSolutionFileContent . Append (
" " + CurProjectGUID + "." + SolutionConfigCombination . VCSolutionConfigAndPlatformName + ".Deploy.0 = " + ProjectConfigAndPlatformPair + ProjectFileGenerator . NewLine ) ;
}
}
2014-03-14 14:13:41 -04:00
}
2015-09-24 12:37:21 -04:00
CombinationsThatWereMatchedToProjects . Add ( SolutionConfigCombination ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
// Check for problems
2016-03-08 09:00:48 -05:00
foreach ( VCSolutionConfigCombination SolutionConfigCombination in SolutionConfigCombinations )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( ! CombinationsThatWereMatchedToProjects . Contains ( SolutionConfigCombination ) )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
throw new BuildException ( "Unable to find a ProjectTarget that matches the solution configuration/platform mapping: " + SolutionConfigCombination . Configuration . ToString ( ) + ", " + SolutionConfigCombination . Platform . ToString ( ) + ", " + SolutionConfigCombination . TargetConfigurationName ) ;
2014-03-14 14:13:41 -04:00
}
}
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" EndGlobalSection" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
// Setup other solution properties
{
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" GlobalSection(SolutionProperties) = preSolution" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
// HideSolutionNode sets whether or not the top-level solution entry is completely hidden in the UI.
// We don't want that, as we need users to be able to right click on the solution tree item.
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" HideSolutionNode = FALSE" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" EndGlobalSection" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
// Solution directory hierarchy
{
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" GlobalSection(NestedProjects) = preSolution" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
// Every entry in this section is in the format "Guid1 = Guid2". Guid1 is the child project (or solution
// filter)'s GUID, and Guid2 is the solution filter directory to parent the child project (or solution
// filter) to. This sets up the hierarchical solution explorer tree for all solution folders and projects.
System . Action < StringBuilder /* VCSolutionFileContent */ , List < MasterProjectFolder > /* Folders */ > FolderProcessorFunction = null ;
2015-09-24 12:37:21 -04:00
FolderProcessorFunction = ( LocalVCSolutionFileContent , LocalMasterProjectFolders ) = >
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
foreach ( VisualStudioSolutionFolder CurFolder in LocalMasterProjectFolders )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
string CurFolderGUIDString = CurFolder . FolderGUID . ToString ( "B" ) . ToUpperInvariant ( ) ;
2014-03-14 14:13:41 -04:00
2015-09-24 12:37:21 -04:00
foreach ( MSBuildProjectFile ChildProject in CurFolder . ChildProjects )
2014-03-14 14:13:41 -04:00
{
// e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}"
LocalVCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" " + ChildProject . ProjectGUID . ToString ( "B" ) . ToUpperInvariant ( ) + " = " + CurFolderGUIDString + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
2015-09-24 12:37:21 -04:00
foreach ( VisualStudioSolutionFolder SubFolder in CurFolder . SubFolders )
2014-03-14 14:13:41 -04:00
{
// e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}"
LocalVCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" " + SubFolder . FolderGUID . ToString ( "B" ) . ToUpperInvariant ( ) + " = " + CurFolderGUIDString + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
// Recurse into subfolders
2015-09-24 12:37:21 -04:00
FolderProcessorFunction ( LocalVCSolutionFileContent , CurFolder . SubFolders ) ;
2014-03-14 14:13:41 -04:00
}
} ;
2015-09-24 12:37:21 -04:00
FolderProcessorFunction ( VCSolutionFileContent , RootFolder . SubFolders ) ;
2014-03-14 14:13:41 -04:00
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
" EndGlobalSection" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
}
VCSolutionFileContent . Append (
2015-09-24 12:37:21 -04:00
"EndGlobal" + ProjectFileGenerator . NewLine ) ;
2014-03-14 14:13:41 -04:00
}
// Save the solution file
2015-09-24 12:37:21 -04:00
if ( bSuccess )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
string SolutionFilePath = FileReference . Combine ( MasterProjectPath , SolutionFileName ) . FullName ;
2015-09-24 12:37:21 -04:00
bSuccess = WriteFileIfChanged ( SolutionFilePath , VCSolutionFileContent . ToString ( ) ) ;
2014-03-14 14:13:41 -04:00
}
// Save a solution config file which selects the development editor configuration by default.
2018-05-23 21:04:31 -04:00
if ( bSuccess & & bWriteSolutionOptionFile )
2014-03-14 14:13:41 -04:00
{
2015-05-05 15:32:10 -04:00
// Figure out the filename for the SUO file. VS will automatically import the options from earlier versions if necessary.
2016-09-15 08:33:13 -04:00
FileReference SolutionOptionsFileName ;
2015-05-05 15:32:10 -04:00
switch ( ProjectFileFormat )
2016-03-17 20:30:20 -04:00
{
case VCProjectFileFormat . VisualStudio2012 :
2016-09-15 08:33:13 -04:00
SolutionOptionsFileName = FileReference . Combine ( MasterProjectPath , Path . ChangeExtension ( SolutionFileName , "v11.suo" ) ) ;
2016-03-17 20:30:20 -04:00
break ;
2015-05-05 15:32:10 -04:00
case VCProjectFileFormat . VisualStudio2013 :
2016-09-15 08:33:13 -04:00
SolutionOptionsFileName = FileReference . Combine ( MasterProjectPath , Path . ChangeExtension ( SolutionFileName , "v12.suo" ) ) ;
2015-05-05 15:32:10 -04:00
break ;
case VCProjectFileFormat . VisualStudio2015 :
2016-09-15 08:33:13 -04:00
SolutionOptionsFileName = FileReference . Combine ( MasterProjectPath , ".vs" , Path . GetFileNameWithoutExtension ( SolutionFileName ) , "v14" , ".suo" ) ;
2015-05-05 15:32:10 -04:00
break ;
2016-09-15 08:33:13 -04:00
case VCProjectFileFormat . VisualStudio2017 :
SolutionOptionsFileName = FileReference . Combine ( MasterProjectPath , ".vs" , Path . GetFileNameWithoutExtension ( SolutionFileName ) , "v15" , ".suo" ) ;
break ;
default :
throw new BuildException ( "Unsupported Visual Studio version" ) ;
2015-05-05 15:32:10 -04:00
}
2014-08-14 16:23:28 -04:00
2014-12-10 11:57:57 -05:00
// Check it doesn't exist before overwriting it. Since these files store the user's preferences, it'd be bad form to overwrite them.
2017-01-30 16:52:08 -05:00
if ( ! FileReference . Exists ( SolutionOptionsFileName ) )
2014-12-10 11:57:57 -05:00
{
2017-01-30 16:52:08 -05:00
DirectoryReference . CreateDirectory ( SolutionOptionsFileName . Directory ) ;
2016-09-15 08:33:13 -04:00
2018-04-26 14:11:04 -04:00
VCSolutionOptions Options = new VCSolutionOptions ( ProjectFileFormat ) ;
2014-12-10 11:57:57 -05:00
// Set the default configuration and startup project
VCSolutionConfigCombination DefaultConfig = SolutionConfigCombinations . Find ( x = > x . Configuration = = UnrealTargetConfiguration . Development & & x . Platform = = UnrealTargetPlatform . Win64 & & x . TargetConfigurationName = = "Editor" ) ;
if ( DefaultConfig ! = null )
2014-03-14 14:13:41 -04:00
{
2014-12-10 11:57:57 -05:00
List < VCBinarySetting > Settings = new List < VCBinarySetting > ( ) ;
Settings . Add ( new VCBinarySetting ( "ActiveCfg" , DefaultConfig . VCSolutionConfigAndPlatformName ) ) ;
2015-09-24 12:37:21 -04:00
if ( DefaultProject ! = null )
2014-08-14 16:03:23 -04:00
{
2014-12-10 11:57:57 -05:00
Settings . Add ( new VCBinarySetting ( "StartupProject" , ( ( MSBuildProjectFile ) DefaultProject ) . ProjectGUID . ToString ( "B" ) ) ) ;
2014-08-14 16:03:23 -04:00
}
2014-12-10 11:57:57 -05:00
Options . SetConfiguration ( Settings ) ;
}
// Mark all the projects as closed by default, apart from the startup project
VCSolutionExplorerState ExplorerState = new VCSolutionExplorerState ( ) ;
2018-04-26 14:11:04 -04:00
if ( ProjectFileFormat > = VCProjectFileFormat . VisualStudio2017 )
2014-12-10 11:57:57 -05:00
{
2018-04-26 14:11:04 -04:00
BuildSolutionExplorerState_VS2017 ( RootFolder , "" , ExplorerState , DefaultProject ) ;
2014-12-10 11:57:57 -05:00
}
2018-04-26 14:11:04 -04:00
else
2014-12-10 11:57:57 -05:00
{
2018-04-26 14:11:04 -04:00
BuildSolutionExplorerState_VS2015 ( AllProjectFiles , ExplorerState , DefaultProject , IncludeEnginePrograms ) ;
2014-12-10 11:57:57 -05:00
}
Options . SetExplorerState ( ExplorerState ) ;
// Write the file
2015-09-24 12:37:21 -04:00
if ( Options . Sections . Count > 0 )
2014-12-10 11:57:57 -05:00
{
2016-09-15 08:33:13 -04:00
Options . Write ( SolutionOptionsFileName . FullName ) ;
2014-03-14 14:13:41 -04:00
}
}
}
return bSuccess ;
}
2018-04-26 14:11:04 -04:00
static void BuildSolutionExplorerState_VS2017 ( MasterProjectFolder Folder , string Suffix , VCSolutionExplorerState ExplorerState , ProjectFile DefaultProject )
{
foreach ( ProjectFile Project in Folder . ChildProjects )
{
string ProjectIdentifier = String . Format ( "{0}{1}" , Project . ProjectFilePath . GetFileNameWithoutExtension ( ) , Suffix ) ;
if ( Project = = DefaultProject )
{
ExplorerState . OpenProjects . Add ( new Tuple < string , string [ ] > ( ProjectIdentifier , new string [ ] { ProjectIdentifier } ) ) ;
}
else
{
ExplorerState . OpenProjects . Add ( new Tuple < string , string [ ] > ( ProjectIdentifier , new string [ ] { } ) ) ;
}
}
foreach ( MasterProjectFolder SubFolder in Folder . SubFolders )
{
string SubFolderName = SubFolder . FolderName + Suffix ;
if ( SubFolderName = = "Automation;Programs" )
{
ExplorerState . OpenProjects . Add ( new Tuple < string , string [ ] > ( SubFolderName , new string [ ] { } ) ) ;
}
else
{
ExplorerState . OpenProjects . Add ( new Tuple < string , string [ ] > ( SubFolderName , new string [ ] { SubFolderName } ) ) ;
}
BuildSolutionExplorerState_VS2017 ( SubFolder , ";" + SubFolderName , ExplorerState , DefaultProject ) ;
}
}
static void BuildSolutionExplorerState_VS2015 ( List < ProjectFile > AllProjectFiles , VCSolutionExplorerState ExplorerState , ProjectFile DefaultProject , bool IncludeEnginePrograms )
{
foreach ( ProjectFile ProjectFile in AllProjectFiles )
{
string ProjectName = ProjectFile . ProjectFilePath . GetFileNameWithoutExtension ( ) ;
if ( ProjectFile = = DefaultProject )
{
ExplorerState . OpenProjects . Add ( new Tuple < string , string [ ] > ( ProjectName , new string [ ] { ProjectName } ) ) ;
}
else
{
ExplorerState . OpenProjects . Add ( new Tuple < string , string [ ] > ( ProjectName , new string [ ] { } ) ) ;
}
}
if ( IncludeEnginePrograms )
{
ExplorerState . OpenProjects . Add ( new Tuple < string , string [ ] > ( "Automation" , new string [ 0 ] ) ) ;
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Takes a string and "cleans it up" to make it parsable by the Visual Studio source control provider's file format
/// </summary>
/// <param name="Str">String to clean up</param>
/// <returns>The cleaned up string</returns>
2015-09-24 12:37:21 -04:00
public string CleanupStringForSCC ( string Str )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
string Cleaned = Str ;
2014-03-14 14:13:41 -04:00
// SCC is expecting paths to contain only double-backslashes for path separators. It's a bit weird but we need to do it.
2015-09-24 12:37:21 -04:00
Cleaned = Cleaned . Replace ( Path . DirectorySeparatorChar . ToString ( ) , Path . DirectorySeparatorChar . ToString ( ) + Path . DirectorySeparatorChar . ToString ( ) ) ;
Cleaned = Cleaned . Replace ( Path . AltDirectorySeparatorChar . ToString ( ) , Path . DirectorySeparatorChar . ToString ( ) + Path . DirectorySeparatorChar . ToString ( ) ) ;
2014-03-14 14:13:41 -04:00
// SCC is expecting not to see spaces in these strings, so we'll replace spaces with "\u0020"
2015-09-24 12:37:21 -04:00
Cleaned = Cleaned . Replace ( " " , "\\u0020" ) ;
2014-03-14 14:13:41 -04:00
return Cleaned ;
}
}
}