2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-01-14 12:11:24 -05:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
2020-12-21 23:07:37 -04:00
using EpicGames.Core ;
2021-06-09 12:54:42 -04:00
using UnrealBuildBase ;
2019-01-14 12:11:24 -05:00
namespace UnrealBuildTool
{
/// <summary>
/// Cleans build products and intermediates for the target. This deletes files which are named consistently with the target being built
2020-09-10 15:39:00 -04:00
/// (e.g. UnrealEditor-Foo-Win64-Debug.dll) rather than an actual record of previous build products.
2019-01-14 12:11:24 -05:00
/// </summary>
[ToolMode("Clean", ToolModeOptions.XmlConfig | ToolModeOptions.BuildPlatforms | ToolModeOptions.SingleInstance)]
class CleanMode : ToolMode
{
/// <summary>
/// Whether to avoid cleaning targets
/// </summary>
[CommandLine("-SkipRulesCompile")]
bool bSkipRulesCompile = false ;
2020-03-12 14:08:52 -04:00
/// <summary>
/// Skip pre build targets; just do the main target.
/// </summary>
[CommandLine("-SkipPreBuildTargets")]
public bool bSkipPreBuildTargets = false ;
2019-01-14 12:11:24 -05:00
/// <summary>
/// Main entry point
/// </summary>
/// <param name="Arguments">Command-line arguments</param>
/// <returns>One of the values of ECompilationResult</returns>
public override int Execute ( CommandLineArguments Arguments )
{
Arguments . ApplyTo ( this ) ;
// Create the build configuration object, and read the settings
BuildConfiguration BuildConfiguration = new BuildConfiguration ( ) ;
XmlConfig . ApplyTo ( BuildConfiguration ) ;
Arguments . ApplyTo ( BuildConfiguration ) ;
// Parse all the targets being built
List < TargetDescriptor > TargetDescriptors = TargetDescriptor . ParseCommandLine ( Arguments , BuildConfiguration . bUsePrecompiled , bSkipRulesCompile ) ;
2020-03-12 14:08:52 -04:00
if ( TargetDescriptors . Count = = 0 )
2019-01-14 12:11:24 -05:00
{
throw new BuildException ( "No targets specified to clean" ) ;
}
// Also add implicit descriptors for cleaning UnrealBuildTool
2020-03-12 14:08:52 -04:00
if ( ! BuildConfiguration . bDoNotBuildUHT )
2019-01-14 12:11:24 -05:00
{
const string UnrealHeaderToolTarget = "UnrealHeaderTool" ;
// Get a list of project files to clean UHT for
2020-12-20 20:07:40 -04:00
List < FileReference ? > ProjectFiles = new List < FileReference ? > ( ) ;
2020-03-12 14:08:52 -04:00
foreach ( TargetDescriptor TargetDesc in TargetDescriptors )
2019-01-14 12:11:24 -05:00
{
2020-03-12 14:08:52 -04:00
if ( TargetDesc . Name ! = UnrealHeaderToolTarget & & ! RemoteMac . HandlesTargetPlatform ( TargetDesc . Platform ) )
2019-01-14 12:11:24 -05:00
{
2020-03-12 14:08:52 -04:00
if ( ProjectFiles . Count = = 0 )
2019-01-14 12:11:24 -05:00
{
ProjectFiles . Add ( null ) ;
}
2020-03-12 14:08:52 -04:00
if ( TargetDesc . ProjectFile ! = null & & ! ProjectFiles . Contains ( TargetDesc . ProjectFile ) )
2019-01-14 12:11:24 -05:00
{
ProjectFiles . Add ( TargetDesc . ProjectFile ) ;
}
}
}
// Add descriptors for cleaning UHT with all these projects
2020-03-12 14:08:52 -04:00
if ( ProjectFiles . Count > 0 )
2019-01-14 12:11:24 -05:00
{
UnrealTargetConfiguration Configuration = BuildConfiguration . bForceDebugUnrealHeaderTool ? UnrealTargetConfiguration . Debug : UnrealTargetConfiguration . Development ;
string Architecture = UEBuildPlatform . GetBuildPlatform ( BuildHostPlatform . Current . Platform ) . GetDefaultArchitecture ( null ) ;
2020-12-20 20:07:40 -04:00
foreach ( FileReference ? ProjectFile in ProjectFiles )
2019-01-14 12:11:24 -05:00
{
TargetDescriptors . Add ( new TargetDescriptor ( ProjectFile , UnrealHeaderToolTarget , BuildHostPlatform . Current . Platform , Configuration , Architecture , null ) ) ;
}
}
}
// Output the list of targets that we're cleaning
Log . TraceInformation ( "Cleaning {0} binaries..." , StringUtils . FormatList ( TargetDescriptors . Select ( x = > x . Name ) . Distinct ( ) ) ) ;
// Loop through all the targets, and clean them all
HashSet < FileReference > FilesToDelete = new HashSet < FileReference > ( ) ;
HashSet < DirectoryReference > DirectoriesToDelete = new HashSet < DirectoryReference > ( ) ;
2020-03-12 14:08:52 -04:00
for ( int Idx = 0 ; Idx < TargetDescriptors . Count ; + + Idx )
2019-01-14 12:11:24 -05:00
{
2020-03-12 14:08:52 -04:00
TargetDescriptor TargetDescriptor = TargetDescriptors [ Idx ] ;
2019-01-14 12:11:24 -05:00
// Create the rules assembly
RulesAssembly RulesAssembly = RulesCompiler . CreateTargetRulesAssembly ( TargetDescriptor . ProjectFile , TargetDescriptor . Name , bSkipRulesCompile , BuildConfiguration . bUsePrecompiled , TargetDescriptor . ForeignPlugin ) ;
// Create the rules object
ReadOnlyTargetRules Target = new ReadOnlyTargetRules ( RulesAssembly . CreateTargetRules ( TargetDescriptor . Name , TargetDescriptor . Platform , TargetDescriptor . Configuration , TargetDescriptor . Architecture , TargetDescriptor . ProjectFile , TargetDescriptor . AdditionalArguments ) ) ;
2020-03-12 14:08:52 -04:00
if ( ! bSkipPreBuildTargets & & Target . PreBuildTargets . Count > 0 )
{
foreach ( TargetInfo PreBuildTarget in Target . PreBuildTargets )
{
TargetDescriptor NewTarget = TargetDescriptor . FromTargetInfo ( PreBuildTarget ) ;
if ( ! TargetDescriptors . Contains ( NewTarget ) )
{
TargetDescriptors . Add ( NewTarget ) ;
}
}
}
2019-01-14 12:11:24 -05:00
// Find the base folders that can contain binaries
List < DirectoryReference > BaseDirs = new List < DirectoryReference > ( ) ;
2021-06-11 18:20:44 -04:00
BaseDirs . Add ( Unreal . EngineDirectory ) ;
2019-01-14 12:11:24 -05:00
foreach ( FileReference Plugin in Plugins . EnumeratePlugins ( Target . ProjectFile ) )
{
BaseDirs . Add ( Plugin . Directory ) ;
}
if ( Target . ProjectFile ! = null )
{
BaseDirs . Add ( Target . ProjectFile . Directory ) ;
}
// If we're running a precompiled build, remove anything under the engine folder
BaseDirs . RemoveAll ( x = > RulesAssembly . IsReadOnly ( x ) ) ;
// Get all the names which can prefix build products
List < string > NamePrefixes = new List < string > ( ) ;
if ( Target . Type ! = TargetType . Program )
{
NamePrefixes . Add ( UEBuildTarget . GetAppNameForTargetType ( Target . Type ) ) ;
}
NamePrefixes . Add ( Target . Name ) ;
// Get the suffixes for this configuration
List < string > NameSuffixes = new List < string > ( ) ;
if ( Target . Configuration = = Target . UndecoratedConfiguration )
{
NameSuffixes . Add ( "" ) ;
}
NameSuffixes . Add ( String . Format ( "-{0}-{1}" , Target . Platform . ToString ( ) , Target . Configuration . ToString ( ) ) ) ;
if ( ! String . IsNullOrEmpty ( Target . Architecture ) )
{
NameSuffixes . AddRange ( NameSuffixes . ToArray ( ) . Select ( x = > x + Target . Architecture ) ) ;
}
// Add all the makefiles and caches to be deleted
2021-01-08 19:56:07 -04:00
FilesToDelete . Add ( TargetMakefile . GetLocation ( Target . ProjectFile , Target . Name , Target . Platform , Target . Architecture , Target . Configuration ) ) ;
2019-01-14 12:11:24 -05:00
FilesToDelete . UnionWith ( SourceFileMetadataCache . GetFilesToClean ( Target . ProjectFile ) ) ;
// Add all the intermediate folders to be deleted
foreach ( DirectoryReference BaseDir in BaseDirs )
{
foreach ( string NamePrefix in NamePrefixes )
{
2021-01-08 19:56:07 -04:00
DirectoryReference GeneratedCodeDir = DirectoryReference . Combine ( BaseDir , UEBuildTarget . GetPlatformIntermediateFolder ( Target . Platform , Target . Architecture ) , NamePrefix , "Inc" ) ;
2019-01-14 12:11:24 -05:00
if ( DirectoryReference . Exists ( GeneratedCodeDir ) )
{
DirectoriesToDelete . Add ( GeneratedCodeDir ) ;
}
2021-01-08 19:56:07 -04:00
DirectoryReference IntermediateDir = DirectoryReference . Combine ( BaseDir , UEBuildTarget . GetPlatformIntermediateFolder ( Target . Platform , Target . Architecture ) , NamePrefix , Target . Configuration . ToString ( ) ) ;
2019-01-14 12:11:24 -05:00
if ( DirectoryReference . Exists ( IntermediateDir ) )
{
DirectoriesToDelete . Add ( IntermediateDir ) ;
}
}
}
// List of additional files and directories to clean, specified by the target platform
List < FileReference > AdditionalFilesToDelete = new List < FileReference > ( ) ;
List < DirectoryReference > AdditionalDirectoriesToDelete = new List < DirectoryReference > ( ) ;
// Add all the build products from this target
string [ ] NamePrefixesArray = NamePrefixes . Distinct ( ) . ToArray ( ) ;
string [ ] NameSuffixesArray = NameSuffixes . Distinct ( ) . ToArray ( ) ;
foreach ( DirectoryReference BaseDir in BaseDirs )
{
DirectoryReference BinariesDir = DirectoryReference . Combine ( BaseDir , "Binaries" , Target . Platform . ToString ( ) ) ;
2020-03-12 14:08:52 -04:00
if ( DirectoryReference . Exists ( BinariesDir ) )
2019-01-14 12:11:24 -05:00
{
UEBuildPlatform . GetBuildPlatform ( Target . Platform ) . FindBuildProductsToClean ( BinariesDir , NamePrefixesArray , NameSuffixesArray , AdditionalFilesToDelete , AdditionalDirectoriesToDelete ) ;
}
}
// Get all the additional intermediate folders created by this platform
UEBuildPlatform . GetBuildPlatform ( Target . Platform ) . FindAdditionalBuildProductsToClean ( Target , AdditionalFilesToDelete , AdditionalDirectoriesToDelete ) ;
// Add the platform's files and directories to the main list
FilesToDelete . UnionWith ( AdditionalFilesToDelete ) ;
DirectoriesToDelete . UnionWith ( AdditionalDirectoriesToDelete ) ;
}
// Delete all the directories, then all the files. By sorting the list of directories before we delete them, we avoid spamming the log if a parent directory is deleted first.
foreach ( DirectoryReference DirectoryToDelete in DirectoriesToDelete . OrderBy ( x = > x . FullName ) )
{
if ( DirectoryReference . Exists ( DirectoryToDelete ) )
{
Log . TraceVerbose ( " Deleting {0}{1}..." , DirectoryToDelete , Path . DirectorySeparatorChar ) ;
try
{
2020-02-26 09:56:11 -05:00
FileUtils . ForceDeleteDirectory ( DirectoryToDelete ) ;
2019-01-14 12:11:24 -05:00
}
catch ( Exception Ex )
{
throw new BuildException ( Ex , "Unable to delete {0} ({1})" , DirectoryToDelete , Ex . Message . TrimEnd ( ) ) ;
}
}
}
foreach ( FileReference FileToDelete in FilesToDelete . OrderBy ( x = > x . FullName ) )
{
if ( FileReference . Exists ( FileToDelete ) )
{
Log . TraceVerbose ( " Deleting " + FileToDelete ) ;
try
{
2020-02-26 09:56:11 -05:00
FileUtils . ForceDeleteFile ( FileToDelete ) ;
2019-01-14 12:11:24 -05:00
}
catch ( Exception Ex )
{
throw new BuildException ( Ex , "Unable to delete {0} ({1})" , FileToDelete , Ex . Message . TrimEnd ( ) ) ;
}
}
}
// Also clean all the remote targets
2020-03-12 14:08:52 -04:00
for ( int Idx = 0 ; Idx < TargetDescriptors . Count ; Idx + + )
2019-01-14 12:11:24 -05:00
{
TargetDescriptor TargetDescriptor = TargetDescriptors [ Idx ] ;
2020-03-12 14:08:52 -04:00
if ( RemoteMac . HandlesTargetPlatform ( TargetDescriptor . Platform ) )
2019-01-14 12:11:24 -05:00
{
RemoteMac RemoteMac = new RemoteMac ( TargetDescriptor . ProjectFile ) ;
RemoteMac . Clean ( TargetDescriptor ) ;
}
}
return 0 ;
}
}
}