2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.IO ;
using UnrealBuildTool ;
2015-02-09 19:52:57 -05:00
using System.Diagnostics ;
2014-03-14 14:13:41 -04:00
namespace AutomationTool
{
public struct SingleTargetProperties
{
public string TargetName ;
public TargetRules Rules ;
}
/// <summary>
/// Autodetected project properties.
/// </summary>
public class ProjectProperties
{
/// <summary>
/// Full Project path. Must be a .uproject file
/// </summary>
public string RawProjectPath ;
/// <summary>
/// True if the uproject contains source code.
/// </summary>
public bool bIsCodeBasedProject ;
/// <summary>
/// Whether the project uses Steam (todo: substitute with more generic functionality)
/// </summary>
public bool bUsesSteam ;
2015-02-18 09:18:51 -05:00
/// <summary>
/// Whether the project uses CEF3
/// </summary>
public bool bUsesCEF3 ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// Whether the project uses visual Slate UI (as opposed to the low level windowing/messaging which is always used)
/// </summary>
public bool bUsesSlate = true ;
/// <summary>
/// Hack for legacy game styling isses. No new project should ever set this to true
/// Whether the project uses the Slate editor style in game.
/// </summary>
public bool bUsesSlateEditorStyle = false ;
/// <summary>
// By default we use the Release C++ Runtime (CRT), even when compiling Debug builds. This is because the Debug C++
// Runtime isn't very useful when debugging Unreal Engine projects, and linking against the Debug CRT libraries forces
// our third party library dependencies to also be compiled using the Debug CRT (and often perform more slowly.) Often
// it can be inconvenient to require a separate copy of the debug versions of third party static libraries simply
// so that you can debug your program's code.
/// </summary>
public bool bDebugBuildsActuallyUseDebugCRT = false ;
/// <summary>
/// List of all targets detected for this project.
/// </summary>
public Dictionary < TargetRules . TargetType , SingleTargetProperties > Targets = new Dictionary < TargetRules . TargetType , SingleTargetProperties > ( ) ;
2014-05-14 06:42:35 -04:00
/// <summary>
/// List of all Engine ini files for this project
/// </summary>
public Dictionary < UnrealTargetPlatform , ConfigCacheIni > EngineConfigs = new Dictionary < UnrealTargetPlatform , ConfigCacheIni > ( ) ;
2014-05-29 17:38:00 -04:00
/// <summary>
/// List of all Game ini files for this project
/// </summary>
public Dictionary < UnrealTargetPlatform , ConfigCacheIni > GameConfigs = new Dictionary < UnrealTargetPlatform , ConfigCacheIni > ( ) ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// List of all programs detected for this project.
/// </summary>
public List < SingleTargetProperties > Programs = new List < SingleTargetProperties > ( ) ;
2015-02-27 16:10:22 -05:00
/// <summary>
/// Specifies if the target files were generated
/// </summary>
public bool bWasGenerated = false ;
2014-03-14 14:13:41 -04:00
internal ProjectProperties ( )
{
}
}
/// <summary>
/// Project related utility functions.
/// </summary>
public class ProjectUtils
{
private static CaselessDictionary < ProjectProperties > PropertiesCache = new CaselessDictionary < ProjectProperties > ( ) ;
/// <summary>
/// Gets a short project name (QAGame, Elemental, etc)
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <param name="bIsUProjectFile">True if a uproject.</param>
/// <returns>Short project name</returns>
public static string GetShortProjectName ( string RawProjectPath )
{
return CommandUtils . GetFilenameWithoutAnyExtensions ( RawProjectPath ) ;
}
/// <summary>
/// Gets project properties.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <returns>Properties of the project.</returns>
2015-02-27 16:10:22 -05:00
public static ProjectProperties GetProjectProperties ( string RawProjectPath , List < UnrealTargetPlatform > ClientTargetPlatforms = null )
2014-03-14 14:13:41 -04:00
{
string ProjectKey = "UE4" ;
if ( ! String . IsNullOrEmpty ( RawProjectPath ) )
{
ProjectKey = CommandUtils . ConvertSeparators ( PathSeparator . Slash , Path . GetFullPath ( RawProjectPath ) ) ;
}
ProjectProperties Properties ;
if ( PropertiesCache . TryGetValue ( ProjectKey , out Properties ) = = false )
{
2015-02-27 16:10:22 -05:00
Properties = DetectProjectProperties ( RawProjectPath , ClientTargetPlatforms ) ;
2014-03-14 14:13:41 -04:00
PropertiesCache . Add ( ProjectKey , Properties ) ;
}
return Properties ;
}
/// <summary>
/// Checks if the project is a UProject file with source code.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <returns>True if the project is a UProject file with source code.</returns>
public static bool IsCodeBasedUProjectFile ( string RawProjectPath )
{
2015-02-27 16:10:22 -05:00
return GetProjectProperties ( RawProjectPath , null ) . bIsCodeBasedProject ;
2014-03-14 14:13:41 -04:00
}
/// <summary>
/// Returns a path to the client binaries folder.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <param name="Platform">Platform type.</param>
/// <returns>Path to the binaries folder.</returns>
public static string GetProjectClientBinariesFolder ( string ProjectClientBinariesPath , UnrealTargetPlatform Platform = UnrealTargetPlatform . Unknown )
{
if ( Platform ! = UnrealTargetPlatform . Unknown )
{
ProjectClientBinariesPath = CommandUtils . CombinePaths ( ProjectClientBinariesPath , Platform . ToString ( ) ) ;
}
return ProjectClientBinariesPath ;
}
2015-02-27 16:10:22 -05:00
private static bool RequiresTempTarget ( string RawProjectPath , List < UnrealTargetPlatform > ClientTargetPlatforms )
{
2015-03-02 08:34:17 -05:00
// check to see if we already have a Target.cs file
// @todo: Target.cs may not be the same name as the project in the future, so look at a better way to determine this
if ( File . Exists ( Path . Combine ( Path . GetDirectoryName ( RawProjectPath ) , "Source" , Path . GetFileNameWithoutExtension ( RawProjectPath ) + ".Target.cs" ) ) )
{
return false ;
}
// no Target file, now check to see if build settings have changed
2015-03-02 11:22:04 -05:00
List < UnrealTargetPlatform > TargetPlatforms = ClientTargetPlatforms ;
2015-02-27 16:10:22 -05:00
if ( ClientTargetPlatforms = = null | | ClientTargetPlatforms . Count < 1 )
{
2015-03-02 11:22:04 -05:00
// No client target platforms, add all in
TargetPlatforms = new List < UnrealTargetPlatform > ( ) ;
2015-02-27 16:10:22 -05:00
foreach ( UnrealTargetPlatform TargetPlatformType in Enum . GetValues ( typeof ( UnrealTargetPlatform ) ) )
{
if ( TargetPlatformType ! = UnrealTargetPlatform . Unknown )
{
2015-03-02 11:22:04 -05:00
TargetPlatforms . Add ( TargetPlatformType ) ;
2015-02-27 16:10:22 -05:00
}
}
}
2015-03-02 11:22:04 -05:00
2015-03-10 09:50:47 -04:00
// Change the working directory to be the Engine/Source folder. We are running from Engine/Binaries/DotNET
string oldCWD = Directory . GetCurrentDirectory ( ) ;
2015-04-30 14:37:33 -04:00
if ( BuildConfiguration . RelativeEnginePath = = "../../Engine/" )
2015-03-10 09:50:47 -04:00
{
2015-04-30 14:37:33 -04:00
string EngineSourceDirectory = Path . Combine ( UnrealBuildTool . Utils . GetExecutingAssemblyDirectory ( ) , ".." , ".." , ".." , "Engine" , "Source" ) ;
if ( ! Directory . Exists ( EngineSourceDirectory ) ) // only set the directory if it exists, this should only happen if we are launching the editor from an artist sync
{
EngineSourceDirectory = Path . Combine ( UnrealBuildTool . Utils . GetExecutingAssemblyDirectory ( ) , ".." , ".." , ".." , "Engine" , "Binaries" ) ;
}
Directory . SetCurrentDirectory ( EngineSourceDirectory ) ;
2015-03-10 09:50:47 -04:00
}
2015-04-21 17:20:20 -04:00
// Read the project descriptor, and find all the plugins available to this project
ProjectDescriptor Project = ProjectDescriptor . FromFile ( RawProjectPath ) ;
2015-04-21 17:21:49 -04:00
List < PluginInfo > AvailablePlugins = Plugins . ReadAvailablePlugins ( RawProjectPath ) ;
2015-03-10 09:50:47 -04:00
2015-03-02 11:22:04 -05:00
// check the target platforms for any differences in build settings or additional plugins
2015-04-21 17:20:20 -04:00
bool RetVal = false ;
2015-03-02 11:22:04 -05:00
foreach ( UnrealTargetPlatform TargetPlatformType in TargetPlatforms )
2015-02-27 16:10:22 -05:00
{
2015-03-02 11:22:04 -05:00
IUEBuildPlatform BuildPlat = UEBuildPlatform . GetBuildPlatform ( TargetPlatformType , true ) ;
if ( BuildPlat ! = null & & ! ( BuildPlat as UEBuildPlatform ) . HasDefaultBuildConfig ( TargetPlatformType , Path . GetDirectoryName ( RawProjectPath ) ) )
2015-02-27 16:10:22 -05:00
{
2015-03-10 09:50:47 -04:00
RetVal = true ;
break ;
2015-03-02 11:22:04 -05:00
}
2015-03-10 09:50:47 -04:00
2015-04-21 17:20:20 -04:00
// find if there are any plugins enabled or disabled which differ from the default
2015-04-21 17:21:49 -04:00
foreach ( PluginInfo Plugin in AvailablePlugins )
2015-03-02 11:22:04 -05:00
{
2015-04-21 17:20:20 -04:00
if ( UProjectInfo . IsPluginEnabledForProject ( Plugin , Project , TargetPlatformType ) ! = Plugin . Descriptor . bEnabledByDefault )
2015-02-27 16:10:22 -05:00
{
2015-04-21 17:20:20 -04:00
if ( Plugin . Descriptor . Modules . Any ( Module = > Module . IsCompiledInConfiguration ( TargetPlatformType , TargetRules . TargetType . Game ) ) )
2015-03-10 09:50:47 -04:00
{
2015-04-21 17:20:20 -04:00
RetVal = true ;
2015-03-10 09:50:47 -04:00
break ;
}
2015-02-27 16:10:22 -05:00
}
}
}
2015-04-21 17:20:20 -04:00
// Change back to the original directory
Directory . SetCurrentDirectory ( oldCWD ) ;
2015-03-10 09:50:47 -04:00
return RetVal ;
2015-02-27 16:10:22 -05:00
}
private static void GenerateTempTarget ( string RawProjectPath )
{
// read in the template target cs file
var TempCSFile = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . LocalRoot , "Engine" , "Build" , "Target.cs.template" ) ;
string TargetCSFile = File . ReadAllText ( TempCSFile ) ;
// replace {GAME_NAME} with the game name
TargetCSFile = TargetCSFile . Replace ( "{GAME_NAME}" , Path . GetFileNameWithoutExtension ( RawProjectPath ) ) ;
// write out the file in a new Source directory
string FileName = CommandUtils . CombinePaths ( Path . GetDirectoryName ( RawProjectPath ) , "Intermediate" , "Source" , Path . GetFileNameWithoutExtension ( RawProjectPath ) + ".Target.cs" ) ;
if ( ! Directory . Exists ( Path . GetDirectoryName ( FileName ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( FileName ) ) ;
}
File . WriteAllText ( FileName , TargetCSFile ) ;
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Attempts to autodetect project properties.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <returns>Project properties.</returns>
2015-02-27 16:10:22 -05:00
private static ProjectProperties DetectProjectProperties ( string RawProjectPath , List < UnrealTargetPlatform > ClientTargetPlatforms )
2014-03-14 14:13:41 -04:00
{
var Properties = new ProjectProperties ( ) ;
Properties . RawProjectPath = RawProjectPath ;
2015-02-27 16:10:22 -05:00
// detect if the project is content only, but has non-default build settings
2015-03-02 08:34:17 -05:00
List < string > ExtraSearchPaths = null ;
2015-05-13 20:14:59 -04:00
if ( ! string . IsNullOrEmpty ( RawProjectPath ) & & ! GlobalCommandLine . Rocket )
2015-02-27 16:10:22 -05:00
{
2015-03-01 20:32:48 -05:00
if ( RequiresTempTarget ( RawProjectPath , ClientTargetPlatforms ) )
{
GenerateTempTarget ( RawProjectPath ) ;
Properties . bWasGenerated = true ;
ExtraSearchPaths = new List < string > ( ) ;
ExtraSearchPaths . Add ( CommandUtils . CombinePaths ( Path . GetDirectoryName ( RawProjectPath ) , "Intermediate" , "Source" ) ) ;
}
else if ( File . Exists ( Path . Combine ( Path . GetDirectoryName ( RawProjectPath ) , "Intermediate" , "Source" , Path . GetFileNameWithoutExtension ( RawProjectPath ) + ".Target.cs" ) ) )
{
File . Delete ( Path . Combine ( Path . GetDirectoryName ( RawProjectPath ) , "Intermediate" , "Source" , Path . GetFileNameWithoutExtension ( RawProjectPath ) + ".Target.cs" ) ) ;
}
2015-02-27 16:10:22 -05:00
}
2014-03-14 14:13:41 -04:00
if ( CommandUtils . CmdEnv . HasCapabilityToCompile )
{
2015-02-27 16:10:22 -05:00
DetectTargetsForProject ( Properties , ExtraSearchPaths ) ;
2014-06-27 11:48:20 -04:00
Properties . bIsCodeBasedProject = ! CommandUtils . IsNullOrEmpty ( Properties . Targets ) | | ! CommandUtils . IsNullOrEmpty ( Properties . Programs ) ;
2014-03-14 14:13:41 -04:00
}
else
{
// should never ask for engine targets if we can't compile
if ( String . IsNullOrEmpty ( RawProjectPath ) )
{
2015-04-24 08:22:53 -04:00
throw new AutomationException ( "Cannot determine engine targets if we can't compile." ) ;
2014-03-14 14:13:41 -04:00
}
2015-02-27 16:10:22 -05:00
Properties . bIsCodeBasedProject = Properties . bWasGenerated ;
2014-03-14 14:13:41 -04:00
// if there's a Source directory with source code in it, then mark us as having source code
string SourceDir = CommandUtils . CombinePaths ( Path . GetDirectoryName ( RawProjectPath ) , "Source" ) ;
if ( Directory . Exists ( SourceDir ) )
{
string [ ] CppFiles = Directory . GetFiles ( SourceDir , "*.cpp" , SearchOption . AllDirectories ) ;
string [ ] HFiles = Directory . GetFiles ( SourceDir , "*.h" , SearchOption . AllDirectories ) ;
2015-02-27 16:10:22 -05:00
Properties . bIsCodeBasedProject | = ( CppFiles . Length > 0 | | HFiles . Length > 0 ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-05-02 09:54:01 -04:00
// check to see if the uproject loads modules, only if we haven't already determined it is a code based project
if ( ! Properties . bIsCodeBasedProject )
{
string uprojectStr = File . ReadAllText ( RawProjectPath ) ;
Properties . bIsCodeBasedProject = uprojectStr . Contains ( "\"Modules\"" ) ;
}
2014-05-14 06:42:35 -04:00
// Get all ini files
2014-05-14 08:07:42 -04:00
if ( ! String . IsNullOrWhiteSpace ( RawProjectPath ) )
2014-05-14 06:42:35 -04:00
{
2014-05-14 08:07:42 -04:00
CommandUtils . Log ( "Loading ini files for {0}" , RawProjectPath ) ;
2014-05-29 17:38:00 -04:00
var EngineDirectory = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . LocalRoot , "Engine" ) ;
2014-05-14 08:07:42 -04:00
foreach ( UnrealTargetPlatform TargetPlatformType in Enum . GetValues ( typeof ( UnrealTargetPlatform ) ) )
2014-05-14 06:42:35 -04:00
{
2014-05-14 08:07:42 -04:00
if ( TargetPlatformType ! = UnrealTargetPlatform . Unknown )
{
var Config = new ConfigCacheIni ( TargetPlatformType , "Engine" , Path . GetDirectoryName ( RawProjectPath ) , EngineDirectory ) ;
Properties . EngineConfigs . Add ( TargetPlatformType , Config ) ;
}
2014-05-14 06:42:35 -04:00
}
2014-05-29 17:38:00 -04:00
foreach ( UnrealTargetPlatform TargetPlatformType in Enum . GetValues ( typeof ( UnrealTargetPlatform ) ) )
{
if ( TargetPlatformType ! = UnrealTargetPlatform . Unknown )
{
var Config = new ConfigCacheIni ( TargetPlatformType , "Game" , Path . GetDirectoryName ( RawProjectPath ) ) ;
Properties . GameConfigs . Add ( TargetPlatformType , Config ) ;
}
}
2014-05-14 06:42:35 -04:00
}
2014-03-14 14:13:41 -04:00
return Properties ;
}
/// <summary>
/// Gets the project's root binaries folder.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <param name="TargetType">Target type.</param>
/// <param name="bIsUProjectFile">True if uproject file.</param>
/// <returns>Binaries path.</returns>
public static string GetClientProjectBinariesRootPath ( string RawProjectPath , TargetRules . TargetType TargetType , bool bIsCodeBasedProject )
{
var BinPath = String . Empty ;
switch ( TargetType )
{
case TargetRules . TargetType . Program :
BinPath = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . LocalRoot , "Engine" , "Binaries" ) ;
break ;
case TargetRules . TargetType . Client :
case TargetRules . TargetType . Game :
if ( ! bIsCodeBasedProject )
{
BinPath = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . LocalRoot , "Engine" , "Binaries" ) ;
}
else
{
BinPath = CommandUtils . CombinePaths ( CommandUtils . GetDirectoryName ( RawProjectPath ) , "Binaries" ) ;
}
break ;
}
return BinPath ;
}
2014-04-23 18:28:28 -04:00
/// <summary>
/// Gets the location where all rules assemblies should go
/// </summary>
private static string GetRulesAssemblyFolder ( )
{
string RulesFolder ;
if ( GlobalCommandLine . Installed . IsSet )
{
RulesFolder = CommandUtils . CombinePaths ( Path . GetTempPath ( ) , "UAT" , CommandUtils . EscapePath ( CommandUtils . CmdEnv . LocalRoot ) , "Rules" ) ;
}
else
{
RulesFolder = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . EngineSavedFolder , "Rules" ) ;
}
return RulesFolder ;
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Finds all targets for the project.
/// </summary>
/// <param name="Properties">Project properties.</param>
/// <param name="ExtraSearchPaths">Additional search paths.</param>
private static void DetectTargetsForProject ( ProjectProperties Properties , List < string > ExtraSearchPaths = null )
{
Properties . Targets = new Dictionary < TargetRules . TargetType , SingleTargetProperties > ( ) ;
string TargetsDllFilename ;
string FullProjectPath = null ;
var GameFolders = new List < string > ( ) ;
2014-04-23 18:28:28 -04:00
var RulesFolder = GetRulesAssemblyFolder ( ) ;
2014-03-14 14:13:41 -04:00
if ( ! String . IsNullOrEmpty ( Properties . RawProjectPath ) )
{
CommandUtils . Log ( "Looking for targets for project {0}" , Properties . RawProjectPath ) ;
2014-04-23 18:28:28 -04:00
TargetsDllFilename = CommandUtils . CombinePaths ( RulesFolder , String . Format ( "UATRules{0}.dll" , Properties . RawProjectPath . GetHashCode ( ) ) ) ;
2014-03-14 14:13:41 -04:00
FullProjectPath = CommandUtils . GetDirectoryName ( Properties . RawProjectPath ) ;
GameFolders . Add ( FullProjectPath ) ;
CommandUtils . Log ( "Searching for target rule files in {0}" , FullProjectPath ) ;
}
else
{
2014-04-23 18:28:28 -04:00
TargetsDllFilename = CommandUtils . CombinePaths ( RulesFolder , String . Format ( "UATRules{0}.dll" , "_BaseEngine_" ) ) ;
2014-03-14 14:13:41 -04:00
}
RulesCompiler . SetAssemblyNameAndGameFolders ( TargetsDllFilename , GameFolders ) ;
// the UBT code assumes a certain CWD, but artists don't have this CWD.
var SourceDir = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . LocalRoot , "Engine" , "Source" ) ;
bool DirPushed = false ;
if ( CommandUtils . DirectoryExists_NoExceptions ( SourceDir ) )
{
CommandUtils . PushDir ( SourceDir ) ;
DirPushed = true ;
}
var TargetScripts = RulesCompiler . FindAllRulesSourceFiles ( RulesCompiler . RulesFileType . Target , ExtraSearchPaths ) ;
if ( DirPushed )
{
CommandUtils . PopDir ( ) ;
}
if ( ! CommandUtils . IsNullOrEmpty ( TargetScripts ) )
{
// We only care about project target script so filter out any scripts not in the project folder, or take them all if we are just doing engine stuff
var ProjectTargetScripts = new List < string > ( ) ;
foreach ( var Filename in TargetScripts )
{
var FullScriptPath = CommandUtils . CombinePaths ( Path . GetFullPath ( Filename ) ) ;
if ( FullProjectPath = = null | | FullScriptPath . StartsWith ( FullProjectPath , StringComparison . InvariantCultureIgnoreCase ) )
{
ProjectTargetScripts . Add ( FullScriptPath ) ;
}
}
TargetScripts = ProjectTargetScripts ;
}
if ( ! CommandUtils . IsNullOrEmpty ( TargetScripts ) )
{
CommandUtils . LogVerbose ( "Found {0} target rule files:" , TargetScripts . Count ) ;
foreach ( var Filename in TargetScripts )
{
CommandUtils . LogVerbose ( " {0}" , Filename ) ;
}
// Check if the scripts require compilation
bool DoNotCompile = false ;
2014-12-11 03:13:57 -05:00
if ( ! CommandUtils . IsBuildMachine & & ! CheckIfScriptAssemblyIsOutOfDate ( TargetsDllFilename , TargetScripts ) )
2014-03-14 14:13:41 -04:00
{
Log . TraceInformation ( "Targets DLL {0} is up to date." , TargetsDllFilename ) ;
DoNotCompile = true ;
}
if ( ! DoNotCompile & & CommandUtils . FileExists_NoExceptions ( TargetsDllFilename ) )
{
if ( ! CommandUtils . DeleteFile_NoExceptions ( TargetsDllFilename , true ) )
{
DoNotCompile = true ;
CommandUtils . Log ( "Could not delete {0} assuming it is up to date and reusable for a recursive UAT call." , TargetsDllFilename ) ;
}
}
2014-12-11 03:13:57 -05:00
CompileAndLoadTargetsAssembly ( Properties , TargetsDllFilename , DoNotCompile , TargetScripts ) ;
2014-03-14 14:13:41 -04:00
}
}
/// <summary>
/// Optionally compiles and loads target rules assembly.
/// </summary>
/// <param name="Properties"></param>
/// <param name="TargetsDllFilename"></param>
/// <param name="DoNotCompile"></param>
/// <param name="TargetScripts"></param>
private static void CompileAndLoadTargetsAssembly ( ProjectProperties Properties , string TargetsDllFilename , bool DoNotCompile , List < string > TargetScripts )
{
CommandUtils . Log ( "Compiling targets DLL: {0}" , TargetsDllFilename ) ;
2015-02-04 04:24:32 -05:00
2014-03-14 14:13:41 -04:00
var ReferencedAssemblies = new List < string > ( )
{
"System.dll" ,
"System.Core.dll" ,
"System.Xml.dll" ,
typeof ( UnrealBuildTool . UnrealBuildTool ) . Assembly . Location
} ;
var TargetsDLL = DynamicCompilation . CompileAndLoadAssembly ( TargetsDllFilename , TargetScripts , ReferencedAssemblies , null , DoNotCompile ) ;
2015-05-11 14:09:46 -04:00
var DummyTargetInfo = new TargetInfo ( BuildHostPlatform . Current . Platform , UnrealTargetConfiguration . Development ) ;
2014-03-14 14:13:41 -04:00
var AllCompiledTypes = TargetsDLL . GetTypes ( ) ;
foreach ( Type TargetType in AllCompiledTypes )
{
// Find TargetRules but skip all "UE4Editor", "UE4Game" targets.
2014-12-11 03:13:57 -05:00
if ( typeof ( TargetRules ) . IsAssignableFrom ( TargetType ) )
2014-03-14 14:13:41 -04:00
{
// Create an instance of this type
CommandUtils . LogVerbose ( "Creating target rules object: {0}" , TargetType . Name ) ;
var Rules = Activator . CreateInstance ( TargetType , DummyTargetInfo ) as TargetRules ;
CommandUtils . LogVerbose ( "Adding target: {0} ({1})" , TargetType . Name , Rules . Type ) ;
SingleTargetProperties TargetData ;
TargetData . TargetName = GetTargetName ( TargetType ) ;
Rules . TargetName = TargetData . TargetName ;
TargetData . Rules = Rules ;
if ( Rules . Type = = TargetRules . TargetType . Program )
{
Properties . Programs . Add ( TargetData ) ;
}
else
{
Properties . Targets . Add ( Rules . Type , TargetData ) ;
}
Properties . bUsesSteam | = Rules . bUsesSteam ;
2015-02-18 09:18:51 -05:00
Properties . bUsesCEF3 | = Rules . bUsesCEF3 ;
2014-03-14 14:13:41 -04:00
Properties . bUsesSlate | = Rules . bUsesSlate ;
Properties . bDebugBuildsActuallyUseDebugCRT | = Rules . bDebugBuildsActuallyUseDebugCRT ;
Properties . bUsesSlateEditorStyle | = Rules . bUsesSlateEditorStyle ;
}
}
}
/// <summary>
/// Checks if any of the script files in newer than the generated assembly.
/// </summary>
/// <param name="TargetsDllFilename"></param>
/// <param name="TargetScripts"></param>
/// <returns>True if the generated assembly is out of date.</returns>
private static bool CheckIfScriptAssemblyIsOutOfDate ( string TargetsDllFilename , List < string > TargetScripts )
{
var bOutOfDate = false ;
var AssemblyInfo = new FileInfo ( TargetsDllFilename ) ;
if ( AssemblyInfo . Exists )
{
foreach ( var ScriptFilename in TargetScripts )
{
var ScriptInfo = new FileInfo ( ScriptFilename ) ;
if ( ScriptInfo . Exists & & ScriptInfo . LastWriteTimeUtc > AssemblyInfo . LastWriteTimeUtc )
{
bOutOfDate = true ;
break ;
}
}
}
else
{
bOutOfDate = true ;
}
return bOutOfDate ;
}
/// <summary>
/// Converts class type name (usually ends with Target) to a target name (without the postfix).
/// </summary>
/// <param name="TargetRulesType">Tagert class.</param>
/// <returns>Target name</returns>
private static string GetTargetName ( Type TargetRulesType )
{
const string TargetPostfix = "Target" ;
var Name = TargetRulesType . Name ;
if ( Name . EndsWith ( TargetPostfix , StringComparison . InvariantCultureIgnoreCase ) )
{
Name = Name . Substring ( 0 , Name . Length - TargetPostfix . Length ) ;
}
return Name ;
}
2014-04-23 18:28:28 -04:00
/// <summary>
/// Performs initial cleanup of target rules folder
/// </summary>
public static void CleanupFolders ( )
{
CommandUtils . Log ( "Cleaning up project rules folder" ) ;
var RulesFolder = GetRulesAssemblyFolder ( ) ;
if ( CommandUtils . DirectoryExists ( RulesFolder ) )
{
CommandUtils . DeleteDirectoryContents ( RulesFolder ) ;
}
}
2014-03-14 14:13:41 -04:00
}
public class BranchInfo
{
public static TargetRules . TargetType [ ] MonolithicKinds = new TargetRules . TargetType [ ]
{
TargetRules . TargetType . Game ,
TargetRules . TargetType . Client ,
TargetRules . TargetType . Server ,
} ;
2015-02-09 19:52:57 -05:00
[DebuggerDisplay("{GameName}")]
2014-03-14 14:13:41 -04:00
public class BranchUProject
{
public string GameName ;
public string FilePath ;
public ProjectProperties Properties ;
public BranchUProject ( UnrealBuildTool . UProjectInfo InfoEntry )
{
GameName = InfoEntry . GameName ;
//not sure what the heck this path is relative to
FilePath = Path . GetFullPath ( CommandUtils . CombinePaths ( CommandUtils . CmdEnv . LocalRoot , "Engine" , "Binaries" , InfoEntry . FilePath ) ) ;
if ( ! CommandUtils . FileExists_NoExceptions ( FilePath ) )
{
throw new AutomationException ( "Could not resolve relative path corrctly {0} -> {1} which doesn't exist." , InfoEntry . FilePath , FilePath ) ;
}
Properties = ProjectUtils . GetProjectProperties ( Path . GetFullPath ( FilePath ) ) ;
}
public BranchUProject ( )
{
GameName = "UE4" ;
Properties = ProjectUtils . GetProjectProperties ( "" ) ;
if ( ! Properties . Targets . ContainsKey ( TargetRules . TargetType . Editor ) )
{
throw new AutomationException ( "Base UE4 project did not contain an editor target." ) ;
}
}
public TargetRules . GUBPProjectOptions Options ( UnrealTargetPlatform HostPlatform )
{
var Options = new TargetRules . GUBPProjectOptions ( ) ;
if ( Properties . Targets . ContainsKey ( TargetRules . TargetType . Editor ) )
{
var Target = Properties . Targets [ TargetRules . TargetType . Editor ] ;
Options = Target . Rules . GUBP_IncludeProjectInPromotedBuild_EditorTypeOnly ( HostPlatform ) ;
}
2015-03-02 15:07:21 -05:00
else if ( Properties . Targets . ContainsKey ( TargetRules . TargetType . Game ) )
{
var Target = Properties . Targets [ TargetRules . TargetType . Game ] ;
Options = Target . Rules . GUBP_IncludeProjectInPromotedBuild_EditorTypeOnly ( HostPlatform ) ;
}
2014-03-14 14:13:41 -04:00
return Options ;
}
public void Dump ( List < UnrealTargetPlatform > InHostPlatforms )
{
CommandUtils . Log ( " ShortName: " + GameName ) ;
CommandUtils . Log ( " FilePath : " + FilePath ) ;
CommandUtils . Log ( " bIsCodeBasedProject : " + ( Properties . bIsCodeBasedProject ? "YES" : "NO" ) ) ;
CommandUtils . Log ( " bUsesSteam : " + ( Properties . bUsesSteam ? "YES" : "NO" ) ) ;
2015-02-18 09:18:51 -05:00
CommandUtils . Log ( " bUsesCEF3 : " + ( Properties . bUsesCEF3 ? "YES" : "NO" ) ) ;
2014-03-14 14:13:41 -04:00
CommandUtils . Log ( " bUsesSlate : " + ( Properties . bUsesSlate ? "YES" : "NO" ) ) ;
foreach ( var HostPlatform in InHostPlatforms )
{
CommandUtils . Log ( " For Host : " + HostPlatform . ToString ( ) ) ;
if ( Properties . bIsCodeBasedProject )
{
CommandUtils . Log ( " Promoted : " + ( Options ( HostPlatform ) . bIsPromotable ? "YES" : "NO" ) ) ;
CommandUtils . Log ( " SeparatePromotable : " + ( Options ( HostPlatform ) . bSeparateGamePromotion ? "YES" : "NO" ) ) ;
CommandUtils . Log ( " Test With Shared : " + ( Options ( HostPlatform ) . bTestWithShared ? "YES" : "NO" ) ) ;
}
CommandUtils . Log ( " Targets {0}:" , Properties . Targets . Count ) ;
foreach ( var ThisTarget in Properties . Targets )
{
CommandUtils . Log ( " TargetName : " + ThisTarget . Value . TargetName ) ;
CommandUtils . Log ( " Type : " + ThisTarget . Key . ToString ( ) ) ;
CommandUtils . Log ( " bUsesSteam : " + ( ThisTarget . Value . Rules . bUsesSteam ? "YES" : "NO" ) ) ;
2015-02-18 09:18:51 -05:00
CommandUtils . Log ( " bUsesCEF3 : " + ( ThisTarget . Value . Rules . bUsesCEF3 ? "YES" : "NO" ) ) ;
2014-03-14 14:13:41 -04:00
CommandUtils . Log ( " bUsesSlate : " + ( ThisTarget . Value . Rules . bUsesSlate ? "YES" : "NO" ) ) ;
if ( Array . IndexOf ( MonolithicKinds , ThisTarget . Key ) > = 0 )
{
2014-08-18 13:29:39 -04:00
var Platforms = ThisTarget . Value . Rules . GUBP_GetPlatforms_MonolithicOnly ( HostPlatform ) ;
var AdditionalPlatforms = ThisTarget . Value . Rules . GUBP_GetBuildOnlyPlatforms_MonolithicOnly ( HostPlatform ) ;
var AllPlatforms = Platforms . Union ( AdditionalPlatforms ) ;
foreach ( var Plat in AllPlatforms )
2014-03-14 14:13:41 -04:00
{
var Configs = ThisTarget . Value . Rules . GUBP_GetConfigs_MonolithicOnly ( HostPlatform , Plat ) ;
foreach ( var Config in Configs )
{
CommandUtils . Log ( " Config : " + Plat . ToString ( ) + " " + Config . ToString ( ) ) ;
}
}
}
}
2014-04-23 18:27:56 -04:00
CommandUtils . Log ( " Programs {0}:" , Properties . Programs . Count ) ;
foreach ( var ThisTarget in Properties . Programs )
{
bool bInternalToolOnly ;
bool SeparateNode ;
2015-01-06 10:24:25 -05:00
bool CrossCompile ;
2015-03-12 08:29:35 -04:00
bool Tool = ThisTarget . Rules . GUBP_AlwaysBuildWithTools ( HostPlatform , out bInternalToolOnly , out SeparateNode , out CrossCompile ) ;
2014-04-23 18:27:56 -04:00
CommandUtils . Log ( " TargetName : " + ThisTarget . TargetName ) ;
CommandUtils . Log ( " Build With Editor : " + ( ThisTarget . Rules . GUBP_AlwaysBuildWithBaseEditor ( ) ? "YES" : "NO" ) ) ;
CommandUtils . Log ( " Build With Tools : " + ( Tool & & ! bInternalToolOnly ? "YES" : "NO" ) ) ;
CommandUtils . Log ( " Build With Internal Tools : " + ( Tool & & bInternalToolOnly ? "YES" : "NO" ) ) ;
CommandUtils . Log ( " Separate Node : " + ( Tool & & SeparateNode ? "YES" : "NO" ) ) ;
2015-01-06 10:24:25 -05:00
CommandUtils . Log ( " Cross Compile : " + ( Tool & & CrossCompile ? "YES" : "NO" ) ) ;
2014-04-23 18:27:56 -04:00
}
2014-03-14 14:13:41 -04:00
}
}
} ;
public BranchUProject BaseEngineProject = null ;
public List < BranchUProject > CodeProjects = new List < BranchUProject > ( ) ;
public List < BranchUProject > NonCodeProjects = new List < BranchUProject > ( ) ;
2015-02-09 19:52:57 -05:00
public IEnumerable < BranchUProject > AllProjects
{
get { return CodeProjects . Union ( NonCodeProjects ) ; }
}
2014-03-14 14:13:41 -04:00
public BranchInfo ( List < UnrealTargetPlatform > InHostPlatforms )
{
BaseEngineProject = new BranchUProject ( ) ;
var AllProjects = UnrealBuildTool . UProjectInfo . FilterGameProjects ( false , null ) ;
2015-05-06 18:08:37 -04:00
var StartTime = DateTime . Now . ToString ( ) ;
2014-03-14 14:13:41 -04:00
foreach ( var InfoEntry in AllProjects )
{
var UProject = new BranchUProject ( InfoEntry ) ;
if ( UProject . Properties . bIsCodeBasedProject )
{
CodeProjects . Add ( UProject ) ;
}
else
{
NonCodeProjects . Add ( UProject ) ;
// the base project uses BlankProject if it really needs a .uproject file
if ( String . IsNullOrEmpty ( BaseEngineProject . FilePath ) & & UProject . GameName = = "BlankProject" )
{
BaseEngineProject . FilePath = UProject . FilePath ;
}
}
}
2015-05-06 18:08:37 -04:00
var FinishTime = DateTime . Now . ToString ( ) ;
2015-05-07 15:35:40 -04:00
CommandUtils . PrintCSVFile ( String . Format ( "UAT,SortProjects,{0},{1}" , StartTime , FinishTime ) ) ;
2014-04-24 12:08:28 -04:00
if ( String . IsNullOrEmpty ( BaseEngineProject . FilePath ) )
{
2014-10-13 11:37:06 -04:00
throw new AutomationException ( "All branches must have the blank project /Samples/Sandbox/BlankProject" ) ;
2014-04-24 12:08:28 -04:00
}
2014-03-14 14:13:41 -04:00
CommandUtils . Log ( " Base Engine:" ) ;
2015-05-06 18:08:37 -04:00
var StartBranchDump = DateTime . Now . ToString ( ) ;
2014-03-14 14:13:41 -04:00
BaseEngineProject . Dump ( InHostPlatforms ) ;
CommandUtils . Log ( " {0} Code projects:" , CodeProjects . Count ) ;
foreach ( var Proj in CodeProjects )
{
Proj . Dump ( InHostPlatforms ) ;
}
CommandUtils . Log ( " {0} Non-Code projects:" , CodeProjects . Count ) ;
foreach ( var Proj in NonCodeProjects )
{
Proj . Dump ( InHostPlatforms ) ;
}
2015-05-07 15:35:40 -04:00
var FinishBranchDump = DateTime . Now . ToString ( ) ;
CommandUtils . PrintCSVFile ( String . Format ( "UAT,Project Dump,{0},{1}" , StartBranchDump , FinishBranchDump ) ) ;
2014-03-14 14:13:41 -04:00
}
public BranchUProject FindGame ( string GameName )
{
foreach ( var Proj in CodeProjects )
{
if ( Proj . GameName . Equals ( GameName , StringComparison . InvariantCultureIgnoreCase ) )
{
return Proj ;
}
}
foreach ( var Proj in NonCodeProjects )
{
if ( Proj . GameName . Equals ( GameName , StringComparison . InvariantCultureIgnoreCase ) )
{
return Proj ;
}
}
return null ;
}
2015-03-10 14:23:43 -04:00
public BranchUProject FindGameChecked ( string GameName )
{
BranchUProject Project = FindGame ( GameName ) ;
if ( Project = = null )
{
throw new AutomationException ( "Cannot find project '{0}' in branch" , GameName ) ;
}
return Project ;
}
2014-04-23 17:51:55 -04:00
public SingleTargetProperties FindProgram ( string ProgramName )
{
foreach ( var Proj in BaseEngineProject . Properties . Programs )
{
if ( Proj . TargetName . Equals ( ProgramName , StringComparison . InvariantCultureIgnoreCase ) )
{
return Proj ;
}
}
SingleTargetProperties Result ;
Result . TargetName = ProgramName ;
Result . Rules = null ;
return Result ;
}
2014-03-14 14:13:41 -04:00
} ;
}