// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "ILauncherWorker.h" #include "Misc/CommandLine.h" #include "Misc/Paths.h" #include "Launcher/LauncherTask.h" #include "Misc/App.h" /** * class for UAT launcher tasks. */ class FLauncherUATTask : public FLauncherTask { public: FLauncherUATTask( const FString& InCommandLine, const FString& InName, const FString& InDesc, void* InReadPipe, void* InWritePipe, const FString& InEditorExe, FProcHandle& InProcessHandle, ILauncherWorker* InWorker, const FString& InCommandEnd, const FString& InBuildCookRunPreCommand) : FLauncherTask(InName, InDesc) , CommandLine(InCommandLine) , ReadPipe(InReadPipe) , WritePipe(InWritePipe) , EditorExe(InEditorExe) , ProcessHandle(InProcessHandle) , CommandText(InCommandEnd) , PreCommand(InBuildCookRunPreCommand) { InWorker->OnOutputReceived().AddRaw(this, &FLauncherUATTask::HandleOutputReceived); } protected: /** * Performs the actual task. * * @param ChainState - Holds the state of the task chain. * * @return true if the task completed successfully, false otherwise. */ virtual bool PerformTask( FLauncherTaskChainState& ChainState ) override { // spawn a UAT process to cook the data // UAT executable FString ExecutablePath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir() + FString(TEXT("Build")) / TEXT("BatchFiles")); #if PLATFORM_MAC FString Executable = TEXT("RunUAT.command"); #elif PLATFORM_LINUX FString Executable = TEXT("RunUAT.sh"); #else FString Executable = TEXT("RunUAT.bat"); #endif // base UAT command arguments static const FString ConfigStrings[] = { TEXT("Unknown"), TEXT("Debug"), TEXT("DebugGame"), TEXT("Development"), TEXT("Shipping"), TEXT("Test") }; FString UATCommandLine; FString ProjectPath = *ChainState.Profile->GetProjectPath(); ProjectPath = FPaths::ConvertRelativePathToFull(ProjectPath); UATCommandLine = FString::Printf(TEXT("-ScriptsForProject=\"%s\" %s BuildCookRun -project=\"%s\" -noP4 -clientconfig=%s -serverconfig=%s"), *ProjectPath, *PreCommand, *ProjectPath, LexToString(ChainState.Profile->GetBuildConfiguration()), LexToString(ChainState.Profile->GetBuildConfiguration())); // we expect to pass -nocompile to UAT here as we generally expect UAT to be fully compiled. Besides, installed builds don't even have the source to compile UAT scripts. // Only allow UAT to compile scripts dynamically if we pass -development or we have the IsBuildingUAT property set, the latter of which should not allow itself to be set in installed situations. bool bAllowCompile = true; UATCommandLine += (bAllowCompile && (FParse::Param( FCommandLine::Get(), TEXT("development") ) || ChainState.Profile->IsBuildingUAT())) ? TEXT("") : TEXT(" -nocompile"); // we never want to build the editor when launching from the editor or running with an installed engine (which can't rebuild itself) UATCommandLine += GIsEditor || FApp::IsEngineInstalled() ? TEXT(" -nocompileeditor") : TEXT(""); UATCommandLine += FApp::IsEngineInstalled() ? TEXT(" -installed") : TEXT(""); // specify the path to the editor exe if necessary if(EditorExe.Len() > 0) { UATCommandLine += FString::Printf(TEXT(" -unrealexe=\"%s\""), *EditorExe); } // specialized command arguments for this particular task UATCommandLine += CommandLine; // launch UAT and monitor its progress ProcessHandle = FPlatformProcess::CreateProc(*(ExecutablePath / Executable), *UATCommandLine, false, true, true, NULL, 0, *ExecutablePath, WritePipe, ReadPipe); while (FPlatformProcess::IsProcRunning(ProcessHandle) && !EndTextFound) { FPlatformProcess::Sleep(0.25); } if (!EndTextFound && !FPlatformProcess::GetProcReturnCode(ProcessHandle, &Result)) { return false; } return (Result == 0); } void HandleOutputReceived(const FString& InMessage) { if (InMessage.Contains(TEXT("Error:"), ESearchCase::IgnoreCase)) { ++ErrorCounter; } else if (InMessage.Contains(TEXT("Warning:"), ESearchCase::IgnoreCase)) { ++WarningCounter; } EndTextFound |= InMessage.Contains(CommandText); } private: // Command line FString CommandLine; // read and write pipe void* ReadPipe = nullptr; void* WritePipe = nullptr; // The editor executable that UAT should use FString EditorExe; // process handle FProcHandle& ProcessHandle; // The editor executable that UAT should use FString CommandText; // a UAT command to run before BuildCookRun (in the same instance as the BuildCookRun) FString PreCommand; bool EndTextFound = false; };