2022-01-19 15:00:37 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2022-06-07 12:37:53 -04:00
using System ;
2022-01-19 15:00:37 -05:00
using System.IO ;
2022-06-07 12:37:53 -04:00
using System.Reflection ;
using System.Runtime.Serialization ;
2023-05-30 18:38:07 -04:00
using EpicGames.Core ;
2022-03-24 13:28:25 -04:00
using UnrealBuildBase ;
2022-01-19 15:00:37 -05:00
namespace UnrealBuildTool
{
/// <summary>
/// TargetRules extension for low level tests.
/// </summary>
public class TestTargetRules : TargetRules
{
2023-06-29 15:18:42 -04:00
/// <summary>
/// Configuration mapping to control build graph test metadata generation during project files generation.
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "LowLevelTestsSettings")]
2023-12-20 12:39:13 -05:00
public bool bUpdateBuildGraphPropertiesFile { get ; set ; }
2023-06-29 15:18:42 -04:00
2022-04-20 14:24:59 -04:00
/// <summary>
/// Keeps track if low level tests executable must build with the Editor.
/// </summary>
2023-12-20 12:39:13 -05:00
public static bool bTestsRequireEditor { get ; set ; }
2022-04-20 14:24:59 -04:00
/// <summary>
/// Keeps track if low level tests executable must build with the Engine.
/// </summary>
2023-12-20 12:39:13 -05:00
public static bool bTestsRequireEngine { get ; set ; }
2022-04-20 14:24:59 -04:00
/// <summary>
/// Keeps track if low level tests executable must build with the ApplicationCore.
/// </summary>
2023-12-20 12:39:13 -05:00
public static bool bTestsRequireApplicationCore { get ; set ; }
2022-04-20 14:24:59 -04:00
/// <summary>
/// Keeps track if low level tests executable must build with the CoreUObject.
/// </summary>
2023-12-20 12:39:13 -05:00
public static bool bTestsRequireCoreUObject { get ; set ; }
2022-04-20 14:24:59 -04:00
2022-01-19 15:00:37 -05:00
/// <summary>
2022-04-27 12:36:35 -04:00
/// Associated tested target of this test target, if defined.
2022-01-19 15:00:37 -05:00
/// </summary>
2023-09-28 06:52:15 -04:00
public TargetRules ? TestedTarget { get ; private set ; }
2022-01-19 15:00:37 -05:00
/// <summary>
2022-04-27 12:36:35 -04:00
/// Test target override for bCompileAgainstApplicationCore.
2023-02-06 14:27:24 -05:00
/// It is set to true if there is any reference to ApplicationCore in the dependency graph.
2022-04-27 12:36:35 -04:00
/// </summary>
public override bool bCompileAgainstApplicationCore
{
2023-05-30 18:01:50 -04:00
get = > bTestsRequireApplicationCore ;
set = > bTestsRequireApplicationCore = value ;
2022-04-27 12:36:35 -04:00
}
2023-02-06 14:27:24 -05:00
/// <summary>
/// If set to true, it will not compile against ApplicationCore even if "ApplicationCore" is in the dependency graph.
/// </summary>
2023-12-20 12:39:13 -05:00
public bool bNeverCompileAgainstApplicationCore { get ; set ; }
2023-02-06 14:27:24 -05:00
2022-04-27 12:36:35 -04:00
/// <summary>
/// Test target override for bCompileAgainstCoreUObject.
2023-02-06 14:27:24 -05:00
/// It is set to true if there is any reference to CoreUObject in the dependency graph.
2022-04-27 12:36:35 -04:00
/// </summary>
public override bool bCompileAgainstCoreUObject
{
2023-05-30 18:01:50 -04:00
get = > bTestsRequireCoreUObject ;
set = > bTestsRequireCoreUObject = value ;
2022-04-27 12:36:35 -04:00
}
2023-02-06 14:27:24 -05:00
/// <summary>
/// If set to true, it will not compile against CoreUObject even if "CoreUObject" is in the dependency graph.
/// </summary>
2023-12-20 12:39:13 -05:00
public bool bNeverCompileAgainstCoreUObject { get ; set ; }
2023-02-06 14:27:24 -05:00
2022-04-27 12:36:35 -04:00
/// <summary>
/// Test target override for bCompileAgainstEngine.
2023-02-06 14:27:24 -05:00
/// It is set to true if there is any reference to Engine in the dependency graph.
2022-04-27 12:36:35 -04:00
/// </summary>
public override bool bCompileAgainstEngine
{
2023-05-30 18:01:50 -04:00
get = > bTestsRequireEngine ;
set = > bTestsRequireEngine = value ;
2022-04-27 12:36:35 -04:00
}
2023-02-06 14:27:24 -05:00
/// <summary>
/// If set to true, it will not compile against engine even if "Engine" is in the dependency graph.
/// </summary>
2023-12-20 12:39:13 -05:00
public bool bNeverCompileAgainstEngine { get ; set ; }
2023-02-06 14:27:24 -05:00
2022-04-27 12:36:35 -04:00
/// <summary>
/// Test target override for bCompileAgainstEditor.
2023-02-06 14:27:24 -05:00
/// It is set to true if there is any reference to UnrealEd in the dependency graph.
2022-04-27 12:36:35 -04:00
/// </summary>
public override bool bCompileAgainstEditor
{
2023-05-30 18:01:50 -04:00
get = > bTestsRequireEditor ;
set = > bTestsRequireEditor = value ;
2022-04-27 12:36:35 -04:00
}
2023-02-06 14:27:24 -05:00
/// <summary>
/// If set to true, it will not compile against editor even if "UnrealEd" is in the dependency graph.
/// </summary>
2023-12-20 12:39:13 -05:00
public bool bNeverCompileAgainstEditor { get ; set ; }
2023-02-06 14:27:24 -05:00
2022-04-27 12:36:35 -04:00
/// <summary>
2023-02-24 15:27:37 -05:00
/// Whether to stub the platform file.
/// </summary>
public bool bUsePlatformFileStub
2023-05-30 18:01:50 -04:00
{
get = > bUsePlatformFileStubPrivate ;
2023-02-24 15:27:37 -05:00
set
{
bUsePlatformFileStubPrivate = value ;
GlobalDefinitions . Remove ( "UE_LLT_USE_PLATFORM_FILE_STUB=0" ) ;
GlobalDefinitions . Remove ( "UE_LLT_USE_PLATFORM_FILE_STUB=1" ) ;
GlobalDefinitions . Add ( $"UE_LLT_USE_PLATFORM_FILE_STUB={Convert.ToInt32(bUsePlatformFileStubPrivate)}" ) ;
}
}
2023-12-20 12:39:13 -05:00
private bool bUsePlatformFileStubPrivate { get ; set ; }
2023-02-24 15:27:37 -05:00
/// <summary>
/// Whether to mock engine default instances for materials, AI controller etc.
/// </summary>
public bool bMockEngineDefaults
{
2023-05-30 18:01:50 -04:00
get = > bMockEngineDefaultsPrivate ;
2023-02-24 15:27:37 -05:00
set
{
bMockEngineDefaultsPrivate = value ;
GlobalDefinitions . Remove ( "UE_LLT_WITH_MOCK_ENGINE_DEFAULTS=0" ) ;
GlobalDefinitions . Remove ( "UE_LLT_WITH_MOCK_ENGINE_DEFAULTS=1" ) ;
GlobalDefinitions . Add ( $"UE_LLT_WITH_MOCK_ENGINE_DEFAULTS={Convert.ToInt32(bMockEngineDefaultsPrivate)}" ) ;
}
}
2023-12-20 12:39:13 -05:00
private bool bMockEngineDefaultsPrivate { get ; set ; }
2023-02-24 15:27:37 -05:00
/// <summary>
/// Constructor that explicit targets can inherit from.
2022-04-27 12:36:35 -04:00
/// </summary>
2023-12-20 12:39:13 -05:00
/// <param name="target"></param>
public TestTargetRules ( TargetInfo target ) : base ( target )
2022-04-27 12:36:35 -04:00
{
2023-12-20 12:39:13 -05:00
SetupCommonProperties ( target ) ;
2022-04-27 12:36:35 -04:00
2023-05-30 18:59:32 -04:00
ExeBinariesSubFolder = LaunchModuleName = Name + ( ExplicitTestsTarget ? String . Empty : "Tests" ) ;
2022-04-27 12:36:35 -04:00
2023-02-06 14:27:24 -05:00
if ( ExplicitTestsTarget )
2022-04-27 12:36:35 -04:00
{
2022-05-19 16:29:25 -04:00
bBuildInSolutionByDefault = true ;
2023-05-25 16:08:34 -04:00
if ( ProjectFile ! = null )
{
2023-12-20 12:39:13 -05:00
DirectoryReference samplesDirectory = DirectoryReference . Combine ( Unreal . RootDirectory , "Samples" ) ;
2023-05-25 16:08:34 -04:00
if ( ! ProjectFile . IsUnderDirectory ( Unreal . EngineDirectory ) )
{
2023-12-20 12:39:13 -05:00
if ( ProjectFile . IsUnderDirectory ( samplesDirectory ) )
2023-05-25 16:08:34 -04:00
{
SolutionDirectory = Path . Combine ( "Samples" , ProjectFile . Directory . GetDirectoryName ( ) , "LowLevelTests" ) ;
}
else
{
SolutionDirectory = Path . Combine ( "Games" , ProjectFile . Directory . GetDirectoryName ( ) , "LowLevelTests" ) ;
}
}
2023-05-26 16:23:08 -04:00
else
{
SolutionDirectory = "Programs/LowLevelTests" ;
}
2023-05-25 16:08:34 -04:00
}
else
{
SolutionDirectory = "Programs/LowLevelTests" ;
}
2023-02-24 15:27:37 -05:00
// Default to true for explicit targets to reduce compilation times.
// Selective module compilation will automatically detect if Engine is required based on Engine include files in tests.
bNeverCompileAgainstEngine = true ;
2022-04-27 12:36:35 -04:00
}
2022-10-21 18:00:09 -04:00
2023-12-20 12:39:13 -05:00
if ( target . Platform = = UnrealTargetPlatform . Win64 )
2022-10-21 18:00:09 -04:00
{
2024-02-29 13:21:19 -05:00
string outputName = target . Name ;
2023-12-20 12:39:13 -05:00
if ( target . Configuration ! = UndecoratedConfiguration )
2022-10-21 18:00:09 -04:00
{
2023-12-20 12:39:13 -05:00
outputName = outputName + "-" + target . Platform + "-" + target . Configuration ;
2022-10-21 18:00:09 -04:00
}
2022-11-02 14:43:21 -04:00
if ( File ! = null )
{
2023-12-20 12:39:13 -05:00
DirectoryReference outputDirectory ;
2023-06-12 15:00:47 -04:00
if ( ProjectFile ! = null & & File . IsUnderDirectory ( ProjectFile . Directory ) )
{
2023-12-20 12:39:13 -05:00
outputDirectory = UEBuildTarget . GetOutputDirectoryForExecutable ( ProjectFile . Directory , File ) ;
2023-06-12 15:00:47 -04:00
}
else
{
2023-12-20 12:39:13 -05:00
outputDirectory = UEBuildTarget . GetOutputDirectoryForExecutable ( Unreal . EngineDirectory , File ) ;
2023-06-12 15:00:47 -04:00
}
2023-12-20 12:39:13 -05:00
FileReference outputTestFile = FileReference . Combine ( outputDirectory , "Binaries" , target . Platform . ToString ( ) , ExeBinariesSubFolder , $"{outputName}.exe.is_unreal_test" ) ;
PostBuildSteps . Add ( "echo > " + outputTestFile . FullName ) ;
2022-11-02 14:43:21 -04:00
}
2022-10-21 18:00:09 -04:00
}
2022-04-27 12:36:35 -04:00
}
2022-06-07 12:37:53 -04:00
/// <summary>
/// Constructs a valid TestTargetRules instance from an existent TargetRules instance.
/// </summary>
2023-12-20 12:39:13 -05:00
public static TestTargetRules Create ( TargetRules rules , TargetInfo targetInfo )
2022-06-07 12:37:53 -04:00
{
2023-12-20 12:39:13 -05:00
Type testTargetRulesType = typeof ( TestTargetRules ) ;
TestTargetRules testRules = ( TestTargetRules ) FormatterServices . GetUninitializedObject ( testTargetRulesType ) ;
2022-06-07 12:37:53 -04:00
// Initialize the logger before calling the constructor
2023-12-20 12:39:13 -05:00
testRules . Logger = rules . Logger ;
2022-06-07 12:37:53 -04:00
2023-12-20 12:39:13 -05:00
ConstructorInfo ? constructor = testTargetRulesType . GetConstructor ( new Type [ ] { typeof ( TargetRules ) , typeof ( TargetInfo ) } )
? ? throw new BuildException ( "No constructor found on {0} which takes first argument of type TargetRules and second of type TargetInfo." , testTargetRulesType . Name ) ;
2022-06-07 12:37:53 -04:00
try
{
2023-12-20 12:39:13 -05:00
constructor . Invoke ( testRules , new object [ ] { rules , targetInfo } ) ;
2022-06-07 12:37:53 -04:00
}
2023-12-20 12:39:13 -05:00
catch ( Exception ex )
2022-06-07 12:37:53 -04:00
{
2023-12-20 12:39:13 -05:00
throw new BuildException ( ex , "Unable to instantiate instance of '{0}' object type from compiled assembly '{1}'. Unreal Build Tool creates an instance of your module's 'Rules' object in order to find out about your module's requirements. The CLR exception details may provide more information: {2}" , testTargetRulesType . Name , Path . GetFileNameWithoutExtension ( testTargetRulesType . Assembly ? . Location ) , ex . ToString ( ) ) ;
2022-06-07 12:37:53 -04:00
}
2023-12-20 12:39:13 -05:00
return testRules ;
2022-06-07 12:37:53 -04:00
}
2022-04-27 12:36:35 -04:00
/// <summary>
2023-09-28 06:52:15 -04:00
/// Constructor for TestTargetRules based on existing target a.k.a Implicit test target.
2022-04-27 12:36:35 -04:00
/// TestTargetRules is setup as a program and is linked monolithically.
2022-01-19 15:00:37 -05:00
/// It removes a lot of default compilation behavior in order to produce a minimal test environment.
/// </summary>
2023-12-20 12:39:13 -05:00
public TestTargetRules ( TargetRules testedTarget , TargetInfo target ) : base ( target )
2022-01-19 15:00:37 -05:00
{
2023-12-20 12:39:13 -05:00
if ( testedTarget is TestTargetRules )
2022-01-19 15:00:37 -05:00
{
throw new BuildException ( "TestedTarget can't be of type TestTargetRules." ) ;
}
2023-12-20 12:39:13 -05:00
TestedTarget = testedTarget ;
2022-01-19 15:00:37 -05:00
2023-12-20 12:39:13 -05:00
TargetFiles = testedTarget . TargetFiles ;
2023-02-06 14:27:24 -05:00
2023-12-20 12:39:13 -05:00
ExeBinariesSubFolder = Name = testedTarget . Name + "Tests" ;
TargetSourceFile = File = testedTarget . File ;
if ( testedTarget . LaunchModuleName ! = null )
2022-04-27 12:36:35 -04:00
{
2023-12-20 12:39:13 -05:00
LaunchModuleName = testedTarget . LaunchModuleName + "Tests" ;
2022-04-27 12:36:35 -04:00
}
2022-01-19 15:00:37 -05:00
2023-12-20 12:39:13 -05:00
ManifestFileNames . Clear ( ) ;
ManifestFileNames . AddRange ( testedTarget . ManifestFileNames ) ;
2022-03-24 13:28:25 -04:00
2023-12-20 12:39:13 -05:00
WindowsPlatform = testedTarget . WindowsPlatform ;
2022-01-19 15:00:37 -05:00
2023-12-20 12:39:13 -05:00
SetupCommonProperties ( target ) ;
SetupImplicitTestProperties ( testedTarget ) ;
2022-04-27 12:36:35 -04:00
}
2023-12-20 12:39:13 -05:00
private void SetupCommonProperties ( TargetInfo target )
2022-04-27 12:36:35 -04:00
{
2023-02-06 14:27:24 -05:00
IncludeOrderVersion = EngineIncludeOrderVersion . Latest ;
2022-04-27 12:36:35 -04:00
bIsTestTargetOverride = true ;
VSTestRunSettingsFile = FileReference . Combine ( Unreal . EngineDirectory , "Source" , "Programs" , "LowLevelTests" , "vstest.runsettings" ) ;
2022-10-31 20:57:12 -04:00
DefaultBuildSettings = BuildSettingsVersion . Latest ;
2022-04-27 12:36:35 -04:00
2022-01-19 15:00:37 -05:00
Type = TargetType . Program ;
LinkType = TargetLinkType . Monolithic ;
bBuildInSolutionByDefault = false ;
2023-12-20 12:39:13 -05:00
bDeployAfterCompile = target . Platform ! = UnrealTargetPlatform . Android ;
2022-01-19 15:00:37 -05:00
bIsBuildingConsoleApplication = true ;
// Disabling default true flags that aren't necessary for tests
// Lean and Mean mode
bBuildDeveloperTools = false ;
// No localization
bCompileICU = false ;
// No need for shaders by default
bForceBuildShaderFormats = false ;
2023-02-06 14:27:24 -05:00
// Do not compile against the engine, editor etc
2023-09-28 06:52:15 -04:00
bCompileAgainstEngine = bTestsRequireEngine ;
bCompileAgainstEditor = bTestsRequireEditor ;
bCompileAgainstCoreUObject = bTestsRequireCoreUObject ;
bCompileAgainstApplicationCore = bTestsRequireApplicationCore ;
2023-02-06 14:27:24 -05:00
bCompileCEF3 = false ;
// No mixing with Functional Test framework
bForceDisableAutomationTests = true ;
bDebugBuildsActuallyUseDebugCRT = true ;
// Allow logging in shipping
2022-01-19 15:00:37 -05:00
bUseLoggingInShipping = true ;
2022-03-24 13:28:25 -04:00
// Allow exception handling
bForceEnableExceptions = true ;
2023-02-06 14:27:24 -05:00
bBuildWithEditorOnlyData = false ;
bBuildRequiresCookedData = true ;
bBuildDeveloperTools = false ;
2022-01-19 15:00:37 -05:00
// Useful for debugging test failures
2023-12-20 12:39:13 -05:00
if ( target . Configuration = = UnrealTargetConfiguration . Debug )
2022-01-19 15:00:37 -05:00
{
bDebugBuildsActuallyUseDebugCRT = true ;
}
2023-02-06 14:27:24 -05:00
if ( ! ExplicitTestsTarget & & TestedTarget ! = null )
{
GlobalDefinitions . AddRange ( TestedTarget . GlobalDefinitions ) ;
}
2022-01-19 15:00:37 -05:00
GlobalDefinitions . Add ( "STATS=0" ) ;
2022-03-24 13:28:25 -04:00
GlobalDefinitions . Add ( "TEST_FOR_VALID_FILE_SYSTEM_MEMORY=0" ) ;
2023-05-30 18:38:07 -04:00
2023-02-24 15:27:37 -05:00
// LLT Globals
GlobalDefinitions . Add ( "UE_LLT_USE_PLATFORM_FILE_STUB=0" ) ;
GlobalDefinitions . Add ( "UE_LLT_WITH_MOCK_ENGINE_DEFAULTS=0" ) ;
2022-01-19 15:00:37 -05:00
// Platform specific setup
2023-12-20 12:39:13 -05:00
if ( target . Platform = = UnrealTargetPlatform . Android )
2022-01-19 15:00:37 -05:00
{
2023-12-20 12:39:13 -05:00
UndecoratedConfiguration = target . Configuration ;
2022-01-19 15:00:37 -05:00
GlobalDefinitions . Add ( "USE_ANDROID_INPUT=0" ) ;
GlobalDefinitions . Add ( "USE_ANDROID_OPENGL=0" ) ;
GlobalDefinitions . Add ( "USE_ANDROID_LAUNCH=0" ) ;
GlobalDefinitions . Add ( "USE_ANDROID_JNI=0" ) ;
2023-10-03 11:18:54 -04:00
2023-10-27 16:55:01 -04:00
// Workaround for a linker bug when building LowLevelTests for Android
// TODO: This should be written to the intermediate directory, somewhere else not when TargetRules are being created
2023-12-20 12:39:13 -05:00
FileReference versionScriptFile = new FileReference ( Path . GetTempPath ( ) + $"LLTWorkaroundScrip-{Name}.ldscript" ) ;
FileReference . WriteAllTextIfDifferent ( versionScriptFile , "{ local: *; };" ) ;
AdditionalLinkerArguments = " -Wl,--version-script=\"" + versionScriptFile . FullName + "\"" ;
2022-01-19 15:00:37 -05:00
}
2023-12-20 12:39:13 -05:00
else if ( target . Platform = = UnrealTargetPlatform . IOS )
2022-01-19 15:00:37 -05:00
{
2022-04-27 12:36:35 -04:00
bIsBuildingConsoleApplication = false ;
2022-01-19 15:00:37 -05:00
}
}
2023-09-28 06:52:15 -04:00
2023-12-20 12:39:13 -05:00
private void SetupImplicitTestProperties ( TargetRules testedTarget )
2023-09-28 06:52:15 -04:00
{
2023-12-20 12:39:13 -05:00
bool isEditorTestedTarget = ( testedTarget . Type = = TargetType . Editor ) ;
bBuildWithEditorOnlyData = bCompileAgainstEditor = isEditorTestedTarget ;
bBuildDeveloperTools = bCompileAgainstEngine & & isEditorTestedTarget ;
2023-12-01 07:05:33 -05:00
bUsePlatformFileStub = bCompileAgainstEngine ;
bMockEngineDefaults = bCompileAgainstEngine ;
bCompileWithPluginSupport = bCompileAgainstEngine ;
2023-09-28 06:52:15 -04:00
}
2022-01-19 15:00:37 -05:00
}
}