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.IO ;
using System.Threading ;
using System.Reflection ;
2014-08-18 13:29:39 -04:00
using System.Linq ;
2014-03-14 14:13:41 -04:00
using AutomationTool ;
using UnrealBuildTool ;
/// <summary>
/// Helper command used for cooking.
/// </summary>
/// <remarks>
/// Command line parameters used by this command:
/// -clean
/// </remarks>
public partial class Project : CommandUtils
{
#region Cook Command
public static void Cook ( ProjectParams Params )
{
2014-09-18 18:55:01 -04:00
if ( ( ! Params . Cook & & ! ( Params . CookOnTheFly & & ! Params . SkipServer ) ) | | Params . SkipCook )
2014-03-14 14:13:41 -04:00
{
return ;
}
Params . ValidateAndLog ( ) ;
2015-08-20 09:37:11 -04:00
Log ( "********** COOK COMMAND STARTED **********" ) ;
2014-09-18 18:55:01 -04:00
2014-03-14 14:13:41 -04:00
string UE4EditorExe = HostPlatform . Current . GetUE4ExePath ( Params . UE4Exe ) ;
if ( ! FileExists ( UE4EditorExe ) )
{
2014-05-06 15:34:31 -04:00
throw new AutomationException ( "Missing " + UE4EditorExe + " executable. Needs to be built first." ) ;
2014-03-14 14:13:41 -04:00
}
2014-09-18 18:55:01 -04:00
if ( Params . CookOnTheFly & & ! Params . SkipServer )
2014-03-14 14:13:41 -04:00
{
2014-12-17 10:00:26 -05:00
if ( Params . HasDLCName )
{
throw new AutomationException ( "Cook on the fly doesn't support cooking dlc" ) ;
}
2014-09-18 18:55:01 -04:00
if ( Params . ClientTargetPlatforms . Count > 0 )
2014-03-14 14:13:41 -04:00
{
2014-09-18 18:55:01 -04:00
var LogFolderOutsideOfSandbox = GetLogFolderOutsideOfSandbox ( ) ;
if ( ! GlobalCommandLine . Installed )
{
// In the installed runs, this is the same folder as CmdEnv.LogFolder so delete only in not-installed
DeleteDirectory ( LogFolderOutsideOfSandbox ) ;
CreateDirectory ( LogFolderOutsideOfSandbox ) ;
}
2015-07-24 10:21:47 -04:00
String COTFCommandLine = Params . RunCommandline ;
if ( Params . IterativeCooking )
{
COTFCommandLine + = " -iterate" ;
}
2015-08-24 17:43:34 -04:00
if ( Params . UseDebugParamForEditorExe )
{
COTFCommandLine + = " -debug" ;
}
2015-07-24 10:21:47 -04:00
2014-09-18 18:55:01 -04:00
var ServerLogFile = CombinePaths ( LogFolderOutsideOfSandbox , "Server.log" ) ;
Platform ClientPlatformInst = Params . ClientTargetPlatformInstances [ 0 ] ;
string TargetCook = ClientPlatformInst . GetCookPlatform ( false , Params . HasDedicatedServerAndClient , Params . CookFlavor ) ;
2015-07-24 10:21:47 -04:00
ServerProcess = RunCookOnTheFlyServer ( Params . RawProjectPath , Params . NoClient ? "" : ServerLogFile , TargetCook , COTFCommandLine ) ;
2014-09-18 18:55:01 -04:00
if ( ServerProcess ! = null )
{
2015-08-20 09:37:11 -04:00
Log ( "Waiting a few seconds for the server to start..." ) ;
2014-09-18 18:55:01 -04:00
Thread . Sleep ( 5000 ) ;
}
}
else
{
throw new AutomationException ( "Failed to run, client target platform not specified" ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-09-18 18:55:01 -04:00
else
2014-03-14 14:13:41 -04:00
{
2014-09-18 18:55:01 -04:00
var PlatformsToCook = new HashSet < string > ( ) ;
if ( ! Params . NoClient )
2014-03-14 14:13:41 -04:00
{
2014-09-18 18:55:01 -04:00
foreach ( var ClientPlatform in Params . ClientTargetPlatforms )
{
// Use the data platform, sometimes we will copy another platform's data
var DataPlatform = Params . GetCookedDataPlatformForClientTarget ( ClientPlatform ) ;
2015-08-26 14:17:28 -04:00
PlatformsToCook . Add ( Params . GetTargetPlatformInstance ( DataPlatform ) . GetCookPlatform ( false , Params . Client , Params . CookFlavor ) ) ;
2014-09-18 18:55:01 -04:00
}
}
if ( Params . DedicatedServer )
{
foreach ( var ServerPlatform in Params . ServerTargetPlatforms )
{
// Use the data platform, sometimes we will copy another platform's data
var DataPlatform = Params . GetCookedDataPlatformForServerTarget ( ServerPlatform ) ;
PlatformsToCook . Add ( Params . GetTargetPlatformInstance ( DataPlatform ) . GetCookPlatform ( true , false , Params . CookFlavor ) ) ;
}
}
if ( Params . Clean . HasValue & & Params . Clean . Value & & ! Params . IterativeCooking )
{
2015-08-20 09:37:11 -04:00
Log ( "Cleaning cooked data." ) ;
2014-09-18 18:55:01 -04:00
CleanupCookedData ( PlatformsToCook . ToList ( ) , Params ) ;
}
// cook the set of maps, or the run map, or nothing
string [ ] Maps = null ;
if ( Params . HasMapsToCook )
{
Maps = Params . MapsToCook . ToArray ( ) ;
2015-02-04 16:18:11 -05:00
foreach ( var M in Maps )
{
2015-08-20 09:37:11 -04:00
Log ( "HasMapsToCook " + M . ToString ( ) ) ;
2015-02-04 16:18:11 -05:00
}
foreach ( var M in Params . MapsToCook )
{
2015-08-20 09:37:11 -04:00
Log ( "Params.HasMapsToCook " + M . ToString ( ) ) ;
2015-02-04 16:18:11 -05:00
}
2014-09-18 18:55:01 -04:00
}
string [ ] Dirs = null ;
if ( Params . HasDirectoriesToCook )
{
Dirs = Params . DirectoriesToCook . ToArray ( ) ;
}
2014-10-27 19:33:51 -04:00
string InternationalizationPreset = null ;
if ( Params . HasInternationalizationPreset )
{
InternationalizationPreset = Params . InternationalizationPreset ;
}
2014-09-18 18:55:01 -04:00
string [ ] Cultures = null ;
if ( Params . HasCulturesToCook )
{
Cultures = Params . CulturesToCook . ToArray ( ) ;
}
2014-10-27 19:33:51 -04:00
try
2014-12-17 10:00:26 -05:00
{
2015-07-30 11:01:02 -04:00
var CommandletParams = IsBuildMachine ? "-buildmachine -fileopenlog" : "-fileopenlog" ;
2015-02-04 16:18:11 -05:00
if ( Params . UnversionedCookedContent )
{
CommandletParams + = " -unversioned" ;
}
2015-06-09 10:11:45 -04:00
if ( Params . FastCook )
{
CommandletParams + = " -FastCook" ;
}
2014-12-17 10:00:26 -05:00
if ( Params . UseDebugParamForEditorExe )
{
CommandletParams + = " -debug" ;
}
if ( Params . Manifests )
{
CommandletParams + = " -manifests" ;
}
if ( Params . IterativeCooking )
{
CommandletParams + = " -iterate" ;
}
2015-02-04 16:18:11 -05:00
if ( Params . CookMapsOnly )
{
CommandletParams + = " -mapsonly" ;
}
2014-12-15 11:35:52 -05:00
if ( Params . NewCook )
{
CommandletParams + = " -newcook" ;
2014-12-17 10:00:26 -05:00
}
2015-02-04 16:18:11 -05:00
if ( Params . OldCook )
{
CommandletParams + = " -oldcook" ;
}
if ( Params . CookAll )
{
CommandletParams + = " -cookall" ;
}
if ( Params . CookMapsOnly )
{
CommandletParams + = " -mapsonly" ;
}
2014-12-15 11:35:52 -05:00
if ( Params . HasCreateReleaseVersion )
{
CommandletParams + = " -createreleaseversion=" + Params . CreateReleaseVersion ;
}
2015-07-06 10:03:34 -04:00
if ( Params . SkipCookingEditorContent )
{
CommandletParams + = " -skipeditorcontent" ;
}
if ( Params . NumCookersToSpawn ! = 0 )
{
CommandletParams + = " -numcookerstospawn=" + Params . NumCookersToSpawn ;
}
2014-12-17 10:00:26 -05:00
if ( Params . HasDLCName )
{
CommandletParams + = " -dlcname=" + Params . DLCName ;
2015-04-20 11:15:20 -04:00
if ( ! Params . DLCIncludeEngineContent )
{
CommandletParams + = " -errorOnEngineContentUse" ;
}
2014-12-17 10:00:26 -05:00
}
2015-03-30 11:56:48 -04:00
// don't include the based on release version unless we are cooking dlc or creating a new release version
// in this case the based on release version is used in packaging
if ( Params . HasBasedOnReleaseVersion & & ( Params . HasDLCName | | Params . HasCreateReleaseVersion ) )
{
CommandletParams + = " -basedonreleaseversion=" + Params . BasedOnReleaseVersion ;
}
2015-02-13 15:35:16 -05:00
// if we are not going to pak but we specified compressed then compress in the cooker ;)
// otherwise compress the pak files
if ( ! Params . Pak & & ! Params . SkipPak & & Params . Compressed )
{
CommandletParams + = " -compressed" ;
}
2014-12-15 11:35:52 -05:00
if ( Params . HasAdditionalCookerOptions )
{
2014-12-17 10:00:26 -05:00
string FormatedAdditionalCookerParams = Params . AdditionalCookerOptions . TrimStart ( new char [ ] { '\"' , ' ' } ) . TrimEnd ( new char [ ] { '\"' , ' ' } ) ;
2015-03-30 11:56:48 -04:00
CommandletParams + = " " ;
2014-12-15 11:35:52 -05:00
CommandletParams + = FormatedAdditionalCookerParams ;
}
2015-04-02 14:54:35 -04:00
if ( ! Params . NoClient )
{
2015-04-02 18:38:41 -04:00
var MapsList = Maps = = null ? new List < string > ( ) : Maps . ToList ( ) ;
2015-04-02 14:54:35 -04:00
foreach ( var ClientPlatform in Params . ClientTargetPlatforms )
{
var DataPlatform = Params . GetCookedDataPlatformForClientTarget ( ClientPlatform ) ;
CommandletParams + = ( Params . GetTargetPlatformInstance ( DataPlatform ) . GetCookExtraCommandLine ( Params ) ) ;
MapsList . AddRange ( ( Params . GetTargetPlatformInstance ( ClientPlatform ) . GetCookExtraMaps ( ) ) ) ;
}
Maps = MapsList . ToArray ( ) ;
}
2014-10-27 19:33:51 -04:00
CookCommandlet ( Params . RawProjectPath , Params . UE4Exe , Maps , Dirs , InternationalizationPreset , Cultures , CombineCommandletParams ( PlatformsToCook . ToArray ( ) ) , CommandletParams ) ;
2014-12-17 10:00:26 -05:00
}
2014-09-18 18:55:01 -04:00
catch ( Exception Ex )
{
2015-06-09 10:11:45 -04:00
if ( Params . IgnoreCookErrors )
{
LogWarning ( "Ignoring cook failure." ) ;
}
else
{
// Delete cooked data (if any) as it may be incomplete / corrupted.
2015-08-20 09:37:11 -04:00
Log ( "Cook failed. Deleting cooked data." ) ;
2015-06-09 10:11:45 -04:00
CleanupCookedData ( PlatformsToCook . ToList ( ) , Params ) ;
2015-08-20 08:38:09 -04:00
throw new AutomationException ( ExitCode . Error_UnknownCookFailure , Ex , "Cook failed." ) ;
2015-06-09 10:11:45 -04:00
}
2014-03-14 14:13:41 -04:00
}
2015-08-11 17:11:41 -04:00
if ( Params . HasDiffCookedContentPath )
{
try
{
DiffCookedContent ( Params ) ;
}
catch ( Exception Ex )
{
// Delete cooked data (if any) as it may be incomplete / corrupted.
2015-08-20 09:37:11 -04:00
Log ( "Cook failed. Deleting cooked data." ) ;
2015-08-11 17:11:41 -04:00
CleanupCookedData ( PlatformsToCook . ToList ( ) , Params ) ;
2015-08-20 08:38:09 -04:00
throw new AutomationException ( ExitCode . Error_UnknownCookFailure , Ex , "Cook failed." ) ;
2015-08-11 17:11:41 -04:00
}
}
2014-03-14 14:13:41 -04:00
}
2015-08-11 17:11:41 -04:00
2015-08-20 09:37:11 -04:00
Log ( "********** COOK COMMAND COMPLETED **********" ) ;
2014-03-14 14:13:41 -04:00
}
2015-08-11 17:11:41 -04:00
private static void DiffCookedContent ( ProjectParams Params )
{
List < UnrealTargetPlatform > PlatformsToCook = Params . ClientTargetPlatforms ;
string ProjectPath = Path . GetFullPath ( Params . RawProjectPath ) ;
var CookedSandboxesPath = CombinePaths ( GetDirectoryName ( ProjectPath ) , "Saved" , "Cooked" ) ;
for ( int CookPlatformIndex = 0 ; CookPlatformIndex < PlatformsToCook . Count ; + + CookPlatformIndex )
{
// temporary directory to save the pak file to (pak file is usually not local and on network drive)
var TemporaryPakPath = CombinePaths ( GetDirectoryName ( ProjectPath ) , "Saved" , "Temp" , "LocalPKG" ) ;
// extracted files from pak file
var TemporaryFilesPath = CombinePaths ( GetDirectoryName ( ProjectPath ) , "Saved" , "Temp" , "LocalFiles" ) ;
try
{
Directory . Delete ( TemporaryPakPath , true ) ;
Directory . Delete ( TemporaryFilesPath , true ) ;
}
catch ( Exception )
{
2015-08-20 09:37:11 -04:00
Log ( "Failed deleting temporary directories " + TemporaryPakPath + " " + TemporaryFilesPath + " continuing." ) ;
2015-08-11 17:11:41 -04:00
}
Directory . CreateDirectory ( TemporaryPakPath ) ;
Directory . CreateDirectory ( TemporaryFilesPath ) ;
Platform CurrentPlatform = Params . GetTargetPlatformInstance ( PlatformsToCook [ CookPlatformIndex ] ) ;
2015-08-18 16:56:56 -04:00
string SourceCookedContentPath = Params . DiffCookedContentPath ;
List < string > PakFiles = new List < string > ( ) ;
if ( Path . HasExtension ( SourceCookedContentPath ) & & ( ! SourceCookedContentPath . EndsWith ( ".pak" ) ) )
2015-08-11 17:11:41 -04:00
{
2015-08-18 16:56:56 -04:00
// must be a per platform pkg file try this
2015-08-11 17:11:41 -04:00
CurrentPlatform . ExtractPackage ( Params , Params . DiffCookedContentPath , TemporaryPakPath ) ;
2015-08-18 16:56:56 -04:00
// find the pak file
PakFiles = Directory . EnumerateFiles ( TemporaryPakPath , "*.pak" ) . ToList ( ) ;
2015-08-11 17:11:41 -04:00
}
2015-08-18 16:56:56 -04:00
string CookPlatformString = CurrentPlatform . GetCookPlatform ( false , Params . HasDedicatedServerAndClient , Params . CookFlavor ) ;
2015-08-11 17:11:41 -04:00
2015-08-18 16:56:56 -04:00
if ( ! Path . HasExtension ( SourceCookedContentPath ) )
{
// try find the pak or pkg file
string SourceCookedContentPlatformPath = CombinePaths ( SourceCookedContentPath , CookPlatformString ) ;
2015-08-11 17:11:41 -04:00
2015-08-18 16:56:56 -04:00
foreach ( var PakName in Directory . EnumerateFiles ( SourceCookedContentPlatformPath , "*.pak" ) )
{
string TemporaryPakFilename = CombinePaths ( TemporaryPakPath , Path . GetFileName ( PakName ) ) ;
File . Copy ( PakName , TemporaryPakFilename ) ;
PakFiles . Add ( TemporaryPakFilename ) ;
}
}
else if ( SourceCookedContentPath . EndsWith ( ".pak" ) )
{
string TemporaryPakFilename = CombinePaths ( TemporaryPakPath , Path . GetFileName ( SourceCookedContentPath ) ) ;
File . Copy ( SourceCookedContentPath , TemporaryPakFilename ) ;
PakFiles . Add ( TemporaryPakFilename ) ;
}
string FullCookPath = CombinePaths ( CookedSandboxesPath , CookPlatformString ) ;
2015-08-11 17:11:41 -04:00
var UnrealPakExe = CombinePaths ( CmdEnv . LocalRoot , "Engine/Binaries/Win64/UnrealPak.exe" ) ;
2015-08-18 16:56:56 -04:00
2015-08-11 17:11:41 -04:00
foreach ( var Name in PakFiles )
{
string UnrealPakParams = Name + " -Extract " + " " + TemporaryFilesPath ;
RunAndLog ( CmdEnv , UnrealPakExe , UnrealPakParams , Options : ERunOptions . Default | ERunOptions . UTF8Output ) ;
}
2015-08-18 16:56:56 -04:00
const string RootFailedContentDirectory = "\\\\epicgames.net\\root\\Developers\\Daniel.Lamb\\" ;
2015-08-18 16:58:49 -04:00
string FailedContentDirectory = CombinePaths ( RootFailedContentDirectory , CommandUtils . P4Env . BuildRootP4 + CommandUtils . P4Env . ChangelistString , Params . ShortProjectName , CookPlatformString ) ;
2015-08-18 16:56:56 -04:00
2015-08-11 17:11:41 -04:00
// diff the content
List < string > AllFiles = Directory . EnumerateFiles ( FullCookPath , "*.uasset" , System . IO . SearchOption . AllDirectories ) . ToList ( ) ;
AllFiles . AddRange ( Directory . EnumerateFiles ( FullCookPath , "*.map" , System . IO . SearchOption . AllDirectories ) . ToList ( ) ) ;
foreach ( string SourceFilename in AllFiles )
{
// Filename.StartsWith( CookedSandboxesPath );
string RelativeFilename = SourceFilename . Remove ( 0 , FullCookPath . Length ) ;
string DestFilename = TemporaryFilesPath + RelativeFilename ;
byte [ ] SourceFile = File . ReadAllBytes ( SourceFilename ) ;
byte [ ] DestFile = File . ReadAllBytes ( DestFilename ) ;
if ( SourceFile . LongLength = = DestFile . LongLength )
{
for ( long Index = 0 ; Index < SourceFile . LongLength ; + + Index )
{
if ( SourceFile [ Index ] ! = DestFile [ Index ] )
{
2015-08-20 09:37:11 -04:00
Log ( "Diff cooked content failed on file " + SourceFilename + " when comparing against " + DestFilename + " at offset " + Index . ToString ( ) ) ;
2015-08-18 16:56:56 -04:00
string SavedSourceFilename = CombinePaths ( FailedContentDirectory , "Source" + Path . GetFileName ( SourceFilename ) ) ;
string SavedDestFilename = CombinePaths ( FailedContentDirectory , "Dest" + Path . GetFileName ( DestFilename ) ) ;
File . Copy ( SourceFilename , SavedSourceFilename ) ;
File . Copy ( DestFilename , SavedDestFilename ) ;
2015-08-20 09:37:11 -04:00
Log ( "Content temporarily saved to " + SavedSourceFilename + " and " + SavedDestFilename + " at offset " + Index . ToString ( ) ) ;
2015-08-11 17:11:41 -04:00
break ;
}
}
}
else
{
2015-08-20 09:37:11 -04:00
Log ( "Diff cooked content failed on file " + SourceFilename + " when comparing against " + DestFilename + " files are different sizes " + SourceFile . LongLength . ToString ( ) + " " + DestFile . LongLength . ToString ( ) ) ;
2015-08-11 17:11:41 -04:00
}
}
}
}
2014-03-14 14:13:41 -04:00
private static void CleanupCookedData ( List < string > PlatformsToCook , ProjectParams Params )
2014-08-18 13:29:39 -04:00
{
2014-03-14 14:13:41 -04:00
var ProjectPath = Path . GetFullPath ( Params . RawProjectPath ) ;
2014-05-29 16:43:14 -04:00
var CookedSandboxesPath = CombinePaths ( GetDirectoryName ( ProjectPath ) , "Saved" , "Cooked" ) ;
2014-03-14 14:13:41 -04:00
var CleanDirs = new string [ PlatformsToCook . Count ] ;
for ( int DirIndex = 0 ; DirIndex < CleanDirs . Length ; + + DirIndex )
{
2014-05-29 16:43:14 -04:00
CleanDirs [ DirIndex ] = CombinePaths ( CookedSandboxesPath , PlatformsToCook [ DirIndex ] ) ;
2014-03-14 14:13:41 -04:00
}
const bool bQuiet = true ;
DeleteDirectory ( bQuiet , CleanDirs ) ;
}
#endregion
}