2022-07-22 00:12:59 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.IO ;
using UnrealBuildTool ;
using System.Diagnostics ;
using EpicGames.Core ;
using System.Reflection ;
using UnrealBuildBase ;
using System.Runtime.Serialization ;
2022-08-31 10:07:21 -04:00
using System.Collections ;
2023-03-07 21:23:47 -05:00
using Microsoft.Extensions.Logging ;
2022-07-22 00:12:59 -04:00
2023-03-08 14:32:15 -05:00
using static AutomationTool . CommandUtils ;
2022-07-22 00:12:59 -04:00
namespace AutomationTool
{
public class SingleTargetProperties
{
public string TargetName ;
public string TargetClassName ;
public TargetRules Rules ;
}
/// <summary>
/// Autodetected project properties.
/// </summary>
public class ProjectProperties
{
/// <summary>
/// Full Project path. Must be a .uproject file
/// </summary>
public FileReference RawProjectPath ;
/// <summary>
/// True if the uproject contains source code.
/// </summary>
public bool bIsCodeBasedProject ;
/// <summary>
/// List of all targets detected for this project.
/// </summary>
public List < SingleTargetProperties > Targets = new List < SingleTargetProperties > ( ) ;
/// <summary>
/// List of all scripts that were compiled to create the list of Targets
/// </summary>
public List < FileReference > TargetScripts = new List < FileReference > ( ) ;
/// <summary>
/// List of all Engine ini files for this project
/// </summary>
public Dictionary < UnrealTargetPlatform , ConfigHierarchy > EngineConfigs = new Dictionary < UnrealTargetPlatform , ConfigHierarchy > ( ) ;
/// <summary>
/// List of all Game ini files for this project
/// </summary>
public Dictionary < UnrealTargetPlatform , ConfigHierarchy > GameConfigs = new Dictionary < UnrealTargetPlatform , ConfigHierarchy > ( ) ;
/// <summary>
/// List of all programs detected for this project.
/// </summary>
public List < SingleTargetProperties > Programs = new List < SingleTargetProperties > ( ) ;
/// <summary>
/// Specifies if the target files were generated
/// </summary>
public bool bWasGenerated = false ;
internal ProjectProperties ( )
{
}
}
/// <summary>
/// Project related utility functions.
/// </summary>
public class ProjectUtils
{
2022-07-22 00:56:39 -04:00
/// <summary>
/// Struct that acts as a key for the project property cache. Based on these attributes
/// DetectProjectProperties may return different answers, e.g. Some platforms require a
/// codebased project for targets
/// </summary>
2022-08-31 10:07:21 -04:00
struct PropertyCacheKey : IEquatable < PropertyCacheKey >
2022-07-22 00:56:39 -04:00
{
string ProjectName ;
UnrealTargetPlatform [ ] TargetPlatforms ;
UnrealTargetConfiguration [ ] TargetConfigurations ;
public PropertyCacheKey ( string InProjectName , IEnumerable < UnrealTargetPlatform > InTargetPlatforms , IEnumerable < UnrealTargetConfiguration > InTargetConfigurations )
{
ProjectName = InProjectName . ToLower ( ) ;
TargetPlatforms = InTargetPlatforms ! = null ? InTargetPlatforms . ToArray ( ) : new UnrealTargetPlatform [ 0 ] ;
TargetConfigurations = InTargetConfigurations ! = null ? InTargetConfigurations . ToArray ( ) : new UnrealTargetConfiguration [ 0 ] ;
}
2022-08-31 10:07:21 -04:00
public bool Equals ( PropertyCacheKey Other )
{
return ProjectName = = Other . ProjectName & &
StructuralComparisons . StructuralEqualityComparer . Equals ( TargetPlatforms , Other . TargetPlatforms ) & &
StructuralComparisons . StructuralEqualityComparer . Equals ( TargetConfigurations , Other . TargetConfigurations ) ;
}
public override bool Equals ( object Other )
{
return Other is PropertyCacheKey OtherKey & & Equals ( OtherKey ) ;
}
public override int GetHashCode ( )
{
return HashCode . Combine (
ProjectName . GetHashCode ( ) ,
StructuralComparisons . StructuralEqualityComparer . GetHashCode ( TargetPlatforms ) ,
StructuralComparisons . StructuralEqualityComparer . GetHashCode ( TargetConfigurations ) ) ;
}
public static bool operator = = ( PropertyCacheKey A , PropertyCacheKey B )
{
return A . Equals ( B ) ;
}
public static bool operator ! = ( PropertyCacheKey A , PropertyCacheKey B )
{
return ! ( A = = B ) ;
}
2022-07-22 00:56:39 -04:00
}
2023-03-08 12:43:35 -05:00
private static ILogger Logger = > Log . Logger ;
2022-07-22 00:56:39 -04:00
private static Dictionary < PropertyCacheKey , ProjectProperties > PropertiesCache = new Dictionary < PropertyCacheKey , ProjectProperties > ( ) ;
2022-07-22 00:12:59 -04:00
/// <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 ( FileReference RawProjectPath )
{
return CommandUtils . GetFilenameWithoutAnyExtensions ( RawProjectPath . FullName ) ;
}
/// <summary>
/// Gets a short alphanumeric identifier for the project path.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <returns>Project path identifier</returns>
public static string GetProjectPathId ( FileReference RawProjectPath )
{
string UniformProjectPath = FileReference . FindCorrectCase ( RawProjectPath ) . ToNormalizedPath ( ) ;
string ProjectPathHash = ContentHash . MD5 ( Encoding . UTF8 . GetBytes ( UniformProjectPath ) ) . ToString ( ) ;
return String . Format ( "{0}.{1}" , GetShortProjectName ( RawProjectPath ) , ProjectPathHash . Substring ( 0 , 8 ) ) ;
}
/// <summary>
/// Gets project properties.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <returns>Properties of the project.</returns>
public static ProjectProperties GetProjectProperties ( FileReference RawProjectPath , List < UnrealTargetPlatform > ClientTargetPlatforms = null , List < UnrealTargetConfiguration > ClientTargetConfigurations = null , bool AssetNativizationRequested = false )
{
string ProjectKey = "UE4" ;
if ( RawProjectPath ! = null )
{
ProjectKey = CommandUtils . ConvertSeparators ( PathSeparator . Slash , RawProjectPath . FullName ) ;
}
ProjectProperties Properties ;
2022-07-22 00:56:39 -04:00
PropertyCacheKey PropertyKey = new PropertyCacheKey ( ProjectKey , ClientTargetPlatforms , ClientTargetConfigurations ) ;
if ( PropertiesCache . TryGetValue ( PropertyKey , out Properties ) = = false )
2022-07-22 00:12:59 -04:00
{
Properties = DetectProjectProperties ( RawProjectPath , ClientTargetPlatforms , ClientTargetConfigurations , AssetNativizationRequested ) ;
2022-07-22 00:56:39 -04:00
PropertiesCache . Add ( PropertyKey , Properties ) ;
2022-07-22 00:12:59 -04:00
}
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>
2022-08-30 18:18:06 -04:00
public static bool IsCodeBasedUProjectFile ( FileReference RawProjectPath , List < UnrealTargetPlatform > ClientTargetPlatforms = null , List < UnrealTargetConfiguration > ClientTargetConfigurations = null )
2022-07-22 00:12:59 -04:00
{
2022-08-30 18:18:06 -04:00
return GetProjectProperties ( RawProjectPath , ClientTargetPlatforms , ClientTargetConfigurations ) . bIsCodeBasedProject ;
}
/// <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 ( FileReference RawProjectPath , UnrealTargetPlatform ClientTargetPlatform , List < UnrealTargetConfiguration > ClientTargetConfigurations = null )
{
return GetProjectProperties ( RawProjectPath , new List < UnrealTargetPlatform > ( ) { ClientTargetPlatform } , ClientTargetConfigurations ) . bIsCodeBasedProject ;
2022-07-22 00:12:59 -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 DirectoryReference GetProjectClientBinariesFolder ( DirectoryReference ProjectClientBinariesPath , UnrealTargetPlatform Platform )
{
ProjectClientBinariesPath = DirectoryReference . Combine ( ProjectClientBinariesPath , Platform . ToString ( ) ) ;
return ProjectClientBinariesPath ;
}
/// <summary>
/// Attempts to autodetect project properties.
/// </summary>
/// <param name="RawProjectPath">Full project path.</param>
/// <returns>Project properties.</returns>
private static ProjectProperties DetectProjectProperties ( FileReference RawProjectPath , List < UnrealTargetPlatform > ClientTargetPlatforms , List < UnrealTargetConfiguration > ClientTargetConfigurations , bool AssetNativizationRequested )
{
ProjectProperties Properties = new ProjectProperties ( ) ;
Properties . RawProjectPath = RawProjectPath ;
// detect if the project is content only, but has non-default build settings
2022-09-08 11:22:00 -04:00
List < string > ExtraSearchPaths = new ( ) ;
2022-07-22 00:12:59 -04:00
if ( RawProjectPath ! = null )
{
// no Target file, now check to see if build settings have changed
List < UnrealTargetPlatform > TargetPlatforms = ClientTargetPlatforms ;
if ( ClientTargetPlatforms = = null | | ClientTargetPlatforms . Count < 1 )
{
// No client target platforms, add all in
TargetPlatforms = new List < UnrealTargetPlatform > ( ) ;
foreach ( UnrealTargetPlatform TargetPlatformType in UnrealTargetPlatform . GetValidPlatforms ( ) )
{
TargetPlatforms . Add ( TargetPlatformType ) ;
}
}
List < UnrealTargetConfiguration > TargetConfigurations = ClientTargetConfigurations ;
if ( TargetConfigurations = = null | | TargetConfigurations . Count < 1 )
{
// No client target configurations, add all in
TargetConfigurations = new List < UnrealTargetConfiguration > ( ) ;
foreach ( UnrealTargetConfiguration TargetConfigurationType in Enum . GetValues ( typeof ( UnrealTargetConfiguration ) ) )
{
if ( TargetConfigurationType ! = UnrealTargetConfiguration . Unknown )
{
TargetConfigurations . Add ( TargetConfigurationType ) ;
}
}
}
2023-08-15 10:07:26 -04:00
if ( NativeProjects . ConditionalMakeTempTargetForHybridProject ( RawProjectPath , TargetPlatforms , Logger ) )
2022-07-22 00:12:59 -04:00
{
Properties . bWasGenerated = true ;
2023-05-16 11:49:47 -04:00
string TempTargetDir = CommandUtils . CombinePaths ( Path . GetDirectoryName ( RawProjectPath . FullName ) , "Intermediate" , "Source" ) ;
ExtraSearchPaths . Add ( TempTargetDir ) ;
2022-07-22 00:12:59 -04:00
}
}
if ( CommandUtils . CmdEnv . HasCapabilityToCompile )
{
DetectTargetsForProject ( Properties , ExtraSearchPaths ) ;
Properties . bIsCodeBasedProject = ! CommandUtils . IsNullOrEmpty ( Properties . Targets ) | | ! CommandUtils . IsNullOrEmpty ( Properties . Programs ) ;
}
else
{
// should never ask for engine targets if we can't compile
if ( RawProjectPath = = null )
{
throw new AutomationException ( "Cannot determine engine targets if we can't compile." ) ;
}
Properties . bIsCodeBasedProject = Properties . bWasGenerated ;
// 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 . FullName ) , "Source" ) ;
if ( Directory . Exists ( SourceDir ) )
{
string [ ] CppFiles = Directory . GetFiles ( SourceDir , "*.cpp" , SearchOption . AllDirectories ) ;
string [ ] HFiles = Directory . GetFiles ( SourceDir , "*.h" , SearchOption . AllDirectories ) ;
Properties . bIsCodeBasedProject | = ( CppFiles . Length > 0 | | HFiles . Length > 0 ) ;
}
}
// check to see if the uproject loads modules, only if we haven't already determined it is a code based project
if ( ! Properties . bIsCodeBasedProject & & RawProjectPath ! = null )
{
string uprojectStr = File . ReadAllText ( RawProjectPath . FullName ) ;
Properties . bIsCodeBasedProject = uprojectStr . Contains ( "\"Modules\"" ) ;
}
// Get all ini files
if ( RawProjectPath ! = null )
{
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Loading ini files for {RawProjectPath}" , RawProjectPath ) ;
2022-07-22 00:12:59 -04:00
foreach ( UnrealTargetPlatform TargetPlatformType in UnrealTargetPlatform . GetValidPlatforms ( ) )
{
ConfigHierarchy EngineConfig = ConfigCache . ReadHierarchy ( ConfigHierarchyType . Engine , RawProjectPath . Directory , TargetPlatformType ) ;
Properties . EngineConfigs . Add ( TargetPlatformType , EngineConfig ) ;
ConfigHierarchy GameConfig = ConfigCache . ReadHierarchy ( ConfigHierarchyType . Game , RawProjectPath . Directory , TargetPlatformType ) ;
Properties . GameConfigs . Add ( TargetPlatformType , GameConfig ) ;
}
}
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 DirectoryReference GetClientProjectBinariesRootPath ( FileReference RawProjectPath , TargetType TargetType , bool bIsCodeBasedProject )
{
DirectoryReference BinPath = null ;
switch ( TargetType )
{
case TargetType . Program :
BinPath = DirectoryReference . Combine ( Unreal . RootDirectory , "Engine" , "Binaries" ) ;
break ;
case TargetType . Client :
case TargetType . Game :
if ( ! bIsCodeBasedProject )
{
BinPath = DirectoryReference . Combine ( Unreal . RootDirectory , "Engine" , "Binaries" ) ;
}
else
{
BinPath = DirectoryReference . Combine ( RawProjectPath . Directory , "Binaries" ) ;
}
break ;
}
return BinPath ;
}
/// <summary>
/// Gets the location where all rules assemblies should go
/// </summary>
private static string GetRulesAssemblyFolder ( )
{
string RulesFolder ;
if ( Unreal . IsEngineInstalled ( ) )
{
RulesFolder = CommandUtils . CombinePaths ( Path . GetTempPath ( ) , "UAT" , CommandUtils . EscapePath ( CommandUtils . CmdEnv . LocalRoot ) , "Rules" ) ;
}
else
{
RulesFolder = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . EngineSavedFolder , "Rules" ) ;
}
return RulesFolder ;
}
/// <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 List < SingleTargetProperties > ( ) ;
FileReference TargetsDllFilename ;
string FullProjectPath = null ;
List < DirectoryReference > GameFolders = new List < DirectoryReference > ( ) ;
DirectoryReference RulesFolder = new DirectoryReference ( GetRulesAssemblyFolder ( ) ) ;
if ( Properties . RawProjectPath ! = null )
{
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Looking for targets for project {Arg0}" , Properties . RawProjectPath ) ;
2022-07-22 00:12:59 -04:00
TargetsDllFilename = FileReference . Combine ( RulesFolder , String . Format ( "UATRules-{0}.dll" , ContentHash . MD5 ( Properties . RawProjectPath . FullName . ToUpperInvariant ( ) ) . ToString ( ) ) ) ;
2022-09-08 11:22:00 -04:00
FullProjectPath = CommandUtils . GetDirectoryName ( Properties . RawProjectPath . FullName ) . Replace ( "\\" , "/" ) ;
// there is a special case of Programs, where the uproject doesn't align with the Source directory, so we redirect to where
// the program's target.cs file(s) are
if ( FullProjectPath . Contains ( "/Programs/" ) )
{
FullProjectPath = FullProjectPath . Replace ( "/Programs/" , "/Source/Programs/" ) ;
}
2022-07-22 00:12:59 -04:00
GameFolders . Add ( new DirectoryReference ( FullProjectPath ) ) ;
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Searching for target rule files in {FullProjectPath}" , FullProjectPath ) ;
2022-07-22 00:12:59 -04:00
}
else
{
TargetsDllFilename = FileReference . Combine ( RulesFolder , String . Format ( "UATRules{0}.dll" , "_BaseEngine_" ) ) ;
}
// the UBT code assumes a certain CWD, but artists don't have this CWD.
string SourceDir = CommandUtils . CombinePaths ( CommandUtils . CmdEnv . LocalRoot , "Engine" , "Source" ) ;
bool DirPushed = false ;
if ( CommandUtils . DirectoryExists_NoExceptions ( SourceDir ) )
{
CommandUtils . PushDir ( SourceDir ) ;
DirPushed = true ;
}
List < DirectoryReference > ExtraSearchDirectories = ( ExtraSearchPaths = = null ) ? null : ExtraSearchPaths . Select ( x = > new DirectoryReference ( x ) ) . ToList ( ) ;
List < FileReference > TargetScripts = Rules . FindAllRulesSourceFiles ( Rules . RulesFileType . Target , GameFolders : GameFolders , ForeignPlugins : null , AdditionalSearchPaths : ExtraSearchDirectories ) ;
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
List < FileReference > ProjectTargetScripts = new List < FileReference > ( ) ;
foreach ( FileReference TargetScript in TargetScripts )
{
if ( FullProjectPath = = null | | TargetScript . IsUnderDirectory ( new DirectoryReference ( FullProjectPath ) ) )
{
2023-03-02 13:21:06 -05:00
// skip target rules that are platform extension or platform group specializations (don't treat _<Platform> targets as extensions if not under a <Platform> directory)
2022-07-22 00:12:59 -04:00
string [ ] TargetPathSplit = TargetScript . GetFileNameWithoutAnyExtensions ( ) . Split ( new char [ ] { '_' } , StringSplitOptions . RemoveEmptyEntries ) ;
2023-03-02 13:21:06 -05:00
if ( TargetPathSplit . Length > 1 & &
( UnrealTargetPlatform . IsValidName ( TargetPathSplit . Last ( ) ) | | UnrealPlatformGroup . IsValidName ( TargetPathSplit . Last ( ) ) ) & &
// platform extension targets will always be under a directory of that platform/group name
TargetScript . ContainsName ( TargetPathSplit . Last ( ) , 0 ) )
2022-07-22 00:12:59 -04:00
{
continue ;
}
ProjectTargetScripts . Add ( TargetScript ) ;
}
}
TargetScripts = ProjectTargetScripts ;
}
if ( ! CommandUtils . IsNullOrEmpty ( TargetScripts ) )
{
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Found {Arg0} target rule files:" , TargetScripts . Count ) ;
2022-07-22 00:12:59 -04:00
foreach ( FileReference Filename in TargetScripts )
{
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( " {Filename}" , Filename ) ;
2022-07-22 00:12:59 -04:00
}
// Check if the scripts require compilation
bool DoNotCompile = false ;
if ( ! CommandUtils . IsBuildMachine & & ! CheckIfScriptAssemblyIsOutOfDate ( TargetsDllFilename , TargetScripts ) )
{
2023-03-08 12:43:35 -05:00
Logger . LogDebug ( "Targets DLL {Filename} is up to date." , TargetsDllFilename ) ;
2022-07-22 00:12:59 -04:00
DoNotCompile = true ;
}
if ( ! DoNotCompile & & CommandUtils . FileExists_NoExceptions ( TargetsDllFilename . FullName ) )
{
if ( ! CommandUtils . DeleteFile_NoExceptions ( TargetsDllFilename . FullName , true ) )
{
DoNotCompile = true ;
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Could not delete {TargetsDllFilename} assuming it is up to date and reusable for a recursive UAT call." , TargetsDllFilename ) ;
2022-07-22 00:12:59 -04:00
}
}
CompileAndLoadTargetsAssembly ( Properties , TargetsDllFilename , DoNotCompile , TargetScripts ) ;
}
}
/// <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 , FileReference TargetsDllFilename , bool DoNotCompile , List < FileReference > TargetScripts )
{
Properties . TargetScripts = new List < FileReference > ( TargetScripts ) ;
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Compiling targets DLL: {TargetsDllFilename}" , TargetsDllFilename ) ;
2022-07-22 00:12:59 -04:00
List < string > ReferencedAssemblies = new List < string > ( )
{
typeof ( UnrealBuildTool . PlatformExports ) . Assembly . Location
} ;
List < string > PreprocessorDefinitions = RulesAssembly . GetPreprocessorDefinitions ( ) ;
Assembly TargetsDLL = DynamicCompilation . CompileAndLoadAssembly ( TargetsDllFilename , new HashSet < FileReference > ( TargetScripts ) , Log . Logger , ReferencedAssemblies , PreprocessorDefinitions , DoNotCompile ) ;
Type [ ] AllCompiledTypes = TargetsDLL . GetTypes ( ) ;
foreach ( Type TargetType in AllCompiledTypes )
{
// Find TargetRules but skip all "UnrealEditor", "UnrealGame" targets.
if ( typeof ( TargetRules ) . IsAssignableFrom ( TargetType ) & & ! TargetType . IsAbstract )
{
string TargetName = GetTargetName ( TargetType ) ;
UnrealArch/UnrealArchitectures changes
- Creates the UnrealArchitectures class, which wraps a list of UnrealArch objects
- UnrealArch is a single architecture, expandable enum-like struct
- There is no more concept of "no/default architecture", there is always a valid active architecture when building
- Most uses of "string Architecture" are replaced with one of the two above, depending if multiple architectures are supported or not
- UnrealArch has some platform-extensions for platform-specific naming (like Linux adds in LinuxName that turns, for instance, Arm64 -> aarch64-unknown-linux-gnueabi, which is used in folder names, etc)
- UnrealArch has bIsX64 which can be used determine intel instruction set (as opposed to arm)
- TargetRules class has an "Architecture" accessor that will return a single architecture if the active architectures is a single architecture, or throw an exception if multiple. This is useful in a majority of the cases where a paltform can only have a single architecture active in TargetRules (microsoft platforms, for instance, will create separate targets when compiling multiple architectures at once)
- Added UnrealArchitectureConfig class, which contains all the architecture information for a platform (what architectures are supported, what ones are currently active for given project, etc)
#preflight 63c81fb5b065224750a1759e
#rb mike.fricker,roman.dzieciol,joe.kirchoff,dmytro.vovk,brandon.schaefer [various parts]
#p4v-preflight-copy 23562471
[CL 23829977 by josh adams in ue5-main branch]
2023-01-24 09:30:28 -05:00
TargetInfo DummyTargetInfo = new TargetInfo ( TargetName , BuildHostPlatform . Current . Platform , UnrealTargetConfiguration . Development , null , Properties . RawProjectPath , null ) ;
2022-07-22 00:12:59 -04:00
// Create an instance of this type
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Creating target rules object: {Arg0}" , TargetType . Name ) ;
2023-01-30 12:31:04 -05:00
TargetRules Rules = TargetRules . Create ( TargetType , DummyTargetInfo , null , null , null , null , Log . Logger ) ;
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Adding target: {Arg0} ({Arg1})" , TargetType . Name , Rules . Type ) ;
2022-07-22 00:12:59 -04:00
SingleTargetProperties TargetData = new SingleTargetProperties ( ) ;
TargetData . TargetName = GetTargetName ( TargetType ) ;
TargetData . TargetClassName = TargetType . FullName ;
TargetData . Rules = Rules ;
if ( Rules . Type = = global :: UnrealBuildTool . TargetType . Program )
{
Properties . Programs . Add ( TargetData ) ;
}
else
{
Properties . Targets . Add ( TargetData ) ;
}
}
}
}
/// <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 ( FileReference TargetsDllFilename , List < FileReference > TargetScripts )
{
bool bOutOfDate = false ;
FileInfo AssemblyInfo = new FileInfo ( TargetsDllFilename . FullName ) ;
if ( AssemblyInfo . Exists )
{
foreach ( FileReference ScriptFilename in TargetScripts )
{
FileInfo ScriptInfo = new FileInfo ( ScriptFilename . FullName ) ;
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" ;
string Name = TargetRulesType . Name ;
if ( Name . EndsWith ( TargetPostfix , StringComparison . InvariantCultureIgnoreCase ) )
{
Name = Name . Substring ( 0 , Name . Length - TargetPostfix . Length ) ;
}
return Name ;
}
/// <summary>
/// Performs initial cleanup of target rules folder
/// </summary>
public static void CleanupFolders ( )
{
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( "Cleaning up project rules folder" ) ;
2022-07-22 00:12:59 -04:00
string RulesFolder = GetRulesAssemblyFolder ( ) ;
if ( CommandUtils . DirectoryExists ( RulesFolder ) )
{
CommandUtils . DeleteDirectoryContents ( RulesFolder ) ;
}
}
/// <summary>
/// Takes a game name (e.g "ShooterGame") and tries to find the path to the project file
/// </summary>
/// <param name="GameName"></param>
/// <returns></returns>
public static FileReference FindProjectFileFromName ( string GameName )
{
// if they passed in a path then easy.
if ( File . Exists ( GameName ) )
{
return new FileReference ( GameName ) ;
}
// Start with the gamename regardless of what they passed in
GameName = Path . GetFileNameWithoutExtension ( GameName ) ;
// Turn Foo into Foo.uproject
string ProjectFile = GameName ;
if ( string . IsNullOrEmpty ( Path . GetExtension ( ProjectFile ) ) )
{
// if project was specified but had no extension then just add it.
ProjectFile = Path . ChangeExtension ( GameName , ".uproject" ) ;
}
// Turn Foo.uproject into Foo/Foo.uproject
ProjectFile = Path . Combine ( GameName , ProjectFile ) ;
GameName = Path . GetFileNameWithoutExtension ( GameName ) ;
// check for sibling to engine
if ( File . Exists ( ProjectFile ) )
{
return new FileReference ( ProjectFile ) ;
}
// Search NativeProjects (sibling folders).
IEnumerable < FileReference > Projects = NativeProjects . EnumerateProjectFiles ( Log . Logger ) ;
FileReference ProjectPath = Projects . Where ( R = > string . Equals ( R . GetFileName ( ) , ProjectFile , StringComparison . OrdinalIgnoreCase ) ) . FirstOrDefault ( ) ;
if ( ProjectPath = = null )
{
// read .uprojectdirs
List < string > SearchPaths = new List < string > ( ) ;
SearchPaths . Add ( "" ) ;
string ProjectDirsFile = Directory . EnumerateFiles ( Environment . CurrentDirectory , "*.uprojectdirs" ) . FirstOrDefault ( ) ;
if ( ProjectDirsFile ! = null )
{
foreach ( string FilePath in File . ReadAllLines ( ProjectDirsFile ) )
{
string Trimmed = FilePath . Trim ( ) ;
if ( ! Trimmed . StartsWith ( "./" , StringComparison . OrdinalIgnoreCase ) & &
! Trimmed . StartsWith ( ";" , StringComparison . OrdinalIgnoreCase ) & &
Trimmed . IndexOfAny ( Path . GetInvalidPathChars ( ) ) < 0 )
{
SearchPaths . Add ( Trimmed ) ;
}
}
string ResolvedFile = SearchPaths . Select ( P = > Path . Combine ( P , ProjectFile ) )
. Where ( P = > File . Exists ( P ) )
. FirstOrDefault ( ) ;
if ( ResolvedFile ! = null )
{
ProjectPath = new FileReference ( ResolvedFile ) ;
}
}
}
// either valid or we're out of ideas...
return ProjectPath ;
}
/// <summary>
/// Full path to the Project executable for the current platform.
/// </summary>
/// <param name="ProjectFile">Path to Project file</param>
/// <param name="TargetType">Target type</param>
/// <param name="TargetPlatform">Target platform</param>
/// <param name="TargetConfiguration">Target build configuration</param>
/// <param name="Cmd">Do you want the console subsystem/commandlet executable?</param>
/// <returns></returns>
public static FileSystemReference GetProjectTarget ( FileReference ProjectFile , UnrealBuildTool . TargetType TargetType , UnrealBuildTool . UnrealTargetPlatform TargetPlatform , UnrealBuildTool . UnrealTargetConfiguration TargetConfiguration = UnrealBuildTool . UnrealTargetConfiguration . Development , bool Cmd = false )
{
ProjectProperties Properties = ProjectUtils . GetProjectProperties ( ProjectFile ) ;
List < SingleTargetProperties > Targets = Properties . Targets . Where ( x = > x . Rules . Type = = TargetType ) . ToList ( ) ;
string TargetName = null ;
switch ( Targets . Count )
{
case 0 :
return null ;
case 1 :
TargetName = Targets . First ( ) . TargetName ;
break ;
default :
Properties . EngineConfigs [ TargetPlatform ] . GetString ( "/Script/BuildSettings.BuildSettings" , "DefaultEditorTarget" , out TargetName ) ;
break ;
}
UnrealArch/UnrealArchitectures changes
- Creates the UnrealArchitectures class, which wraps a list of UnrealArch objects
- UnrealArch is a single architecture, expandable enum-like struct
- There is no more concept of "no/default architecture", there is always a valid active architecture when building
- Most uses of "string Architecture" are replaced with one of the two above, depending if multiple architectures are supported or not
- UnrealArch has some platform-extensions for platform-specific naming (like Linux adds in LinuxName that turns, for instance, Arm64 -> aarch64-unknown-linux-gnueabi, which is used in folder names, etc)
- UnrealArch has bIsX64 which can be used determine intel instruction set (as opposed to arm)
- TargetRules class has an "Architecture" accessor that will return a single architecture if the active architectures is a single architecture, or throw an exception if multiple. This is useful in a majority of the cases where a paltform can only have a single architecture active in TargetRules (microsoft platforms, for instance, will create separate targets when compiling multiple architectures at once)
- Added UnrealArchitectureConfig class, which contains all the architecture information for a platform (what architectures are supported, what ones are currently active for given project, etc)
#preflight 63c81fb5b065224750a1759e
#rb mike.fricker,roman.dzieciol,joe.kirchoff,dmytro.vovk,brandon.schaefer [various parts]
#p4v-preflight-copy 23562471
[CL 23829977 by josh adams in ue5-main branch]
2023-01-24 09:30:28 -05:00
FileReference TargetReceiptFileName = UnrealBuildTool . TargetReceipt . GetDefaultPath ( ProjectFile . Directory , TargetName , TargetPlatform , TargetConfiguration , null ) ;
2022-07-22 00:12:59 -04:00
UnrealBuildTool . TargetReceipt TargetReceipt = UnrealBuildTool . TargetReceipt . Read ( TargetReceiptFileName ) ;
if ( Cmd )
{
return TargetReceipt . LaunchCmd ;
}
if ( TargetPlatform = = UnrealTargetPlatform . Mac )
{
// Remove trailing "/Contents/MacOS/UnrealEngine" to get back to .app directory
return TargetReceipt . Launch . Directory . ParentDirectory . ParentDirectory ;
}
return TargetReceipt . Launch ;
}
}
public class BranchInfo
{
[DebuggerDisplay("{GameName}")]
public class BranchUProject
{
public string GameName ;
public FileReference FilePath ;
private ProjectProperties CachedProperties ;
public ProjectProperties Properties
{
get
{
if ( CachedProperties = = null )
{
CachedProperties = ProjectUtils . GetProjectProperties ( FilePath ) ;
}
return CachedProperties ;
}
}
public BranchUProject ( FileReference ProjectFile )
{
GameName = ProjectFile . GetFileNameWithoutExtension ( ) ;
//not sure what the heck this path is relative to
FilePath = ProjectFile ;
if ( ! CommandUtils . FileExists_NoExceptions ( FilePath . FullName ) )
{
throw new AutomationException ( "Could not resolve relative path corrctly {0} -> {1} which doesn't exist." , ProjectFile , FilePath ) ;
}
}
}
public List < BranchUProject > AllProjects = new List < BranchUProject > ( ) ;
public BranchInfo ( )
{
IEnumerable < FileReference > ProjectFiles = UnrealBuildTool . NativeProjects . EnumerateProjectFiles ( Log . Logger ) ;
foreach ( FileReference InfoEntry in ProjectFiles )
{
AllProjects . Add ( new BranchUProject ( InfoEntry ) ) ;
}
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( " {Arg0} projects:" , AllProjects . Count ) ;
2022-07-22 00:12:59 -04:00
foreach ( BranchUProject Proj in AllProjects )
{
2023-03-08 14:32:15 -05:00
Logger . LogDebug ( " {Arg0}: {Arg1}" , Proj . GameName , Proj . FilePath ) ;
2022-07-22 00:12:59 -04:00
}
}
public BranchUProject FindGame ( string GameName )
{
foreach ( BranchUProject Proj in AllProjects )
{
if ( Proj . GameName . Equals ( GameName , StringComparison . InvariantCultureIgnoreCase ) )
{
return Proj ;
}
}
return null ;
}
public BranchUProject FindGameChecked ( string GameName )
{
BranchUProject Project = FindGame ( GameName ) ;
if ( Project = = null )
{
throw new AutomationException ( "Cannot find project '{0}' in branch" , GameName ) ;
}
return Project ;
}
}
}