int32 BuildPatchToolMain( const TCHAR* CommandLine ) { // Initialize the command line FCommandLine::Set(CommandLine); // Add log devices if (FParse::Param(FCommandLine::Get(), TEXT("stdout"))) { GLog->AddOutputDevice(new FBuildPatchOutputDevice()); } if (FPlatformMisc::IsDebuggerPresent()) { GLog->AddOutputDevice(new FOutputDeviceDebug()); } GLog->Logf(TEXT("BuildPatchToolMain ran with: %s"), CommandLine); FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir(); bool bSuccess = false; FString RootDirectory; FString CloudDirectory; uint32 AppID=0; FString AppName; FString BuildVersion; FString LaunchExe; FString LaunchCommand; FString IgnoreListFile; FString PrereqName; FString PrereqPath; FString PrereqArgs; FString ManifestsList; FString ManifestsFile; float DataAgeThreshold = 0.0f; FString IniFile; TMap<FString, FVariant> CustomFields; bool bCompactify = false; bool bPatchGeneration = true; bool bPreview = false; bool bPatchWithReuseAgeThreshold = true; // Collect all the info from the CommandLine TArray< FString > Tokens, Switches; FCommandLine::Parse(FCommandLine::Get(), Tokens, Switches); if (Switches.Num() > 0) { int32 BuildRootIdx; int32 CloudDirIdx; int32 AppIDIdx; int32 AppNameIdx; int32 BuildVersionIdx; int32 AppLaunchIdx; int32 AppArgsIdx; int32 FileIgnoreListIdx; int32 PrereqNameIdx; int32 PrereqPathIdx; int32 PrereqArgsIdx; int32 ManifestsListIdx; int32 ManifestsFileIdx; int32 DataAgeThresholdIdx; FCommandLineMatcher Matcher; bSuccess = true; Matcher.Command = TEXT("compactify"); bCompactify = Switches.IndexOfByPredicate(Matcher) != INDEX_NONE; bPatchGeneration = !bCompactify; Matcher.Command = TEXT("preview"); bPreview = bCompactify && Switches.IndexOfByPredicate(Matcher) != INDEX_NONE; Matcher.Command = TEXT( "BuildRoot" ); BuildRootIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "CloudDir" ); CloudDirIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "AppID" ); AppIDIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "AppName" ); AppNameIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "BuildVersion" ); BuildVersionIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "AppLaunch" ); AppLaunchIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "AppArgs" ); AppArgsIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "FileIgnoreList" ); FileIgnoreListIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "PrereqName" ); PrereqNameIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "PrereqPath" ); PrereqPathIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT( "PrereqArgs" ); PrereqArgsIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT("ManifestsList"); ManifestsListIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT("ManifestsFile"); ManifestsFileIdx = Switches.IndexOfByPredicate(Matcher); Matcher.Command = TEXT("DataAgeThreshold"); DataAgeThresholdIdx = Switches.IndexOfByPredicate(Matcher); // Check required param indexes bSuccess = bSuccess && CloudDirIdx != INDEX_NONE; if (bPatchGeneration) { bSuccess = bSuccess && BuildRootIdx != INDEX_NONE; bSuccess = bSuccess && AppIDIdx != INDEX_NONE; bSuccess = bSuccess && AppNameIdx != INDEX_NONE; bSuccess = bSuccess && BuildVersionIdx != INDEX_NONE; bSuccess = bSuccess && AppLaunchIdx != INDEX_NONE; bSuccess = bSuccess && AppArgsIdx != INDEX_NONE; } // Get required param values bSuccess = bSuccess && FParse::Value( *Switches[CloudDirIdx], TEXT( "CloudDir=" ), CloudDirectory ); if (bPatchGeneration) { bSuccess = bSuccess && FParse::Value(*Switches[BuildRootIdx], TEXT("BuildRoot="), RootDirectory); bSuccess = bSuccess && FParse::Value(*Switches[AppIDIdx], TEXT("AppID="), AppID); bSuccess = bSuccess && FParse::Value(*Switches[AppNameIdx], TEXT("AppName="), AppName); bSuccess = bSuccess && FParse::Value(*Switches[BuildVersionIdx], TEXT("BuildVersion="), BuildVersion); bSuccess = bSuccess && FParse::Value(*Switches[AppLaunchIdx], TEXT("AppLaunch="), LaunchExe); bSuccess = bSuccess && FParse::Value(*Switches[AppArgsIdx], TEXT("AppArgs="), LaunchCommand); } // Get optional param values if( FileIgnoreListIdx != INDEX_NONE ) { FParse::Value( *Switches[ FileIgnoreListIdx ], TEXT( "FileIgnoreList=" ), IgnoreListFile ); } if( PrereqNameIdx != INDEX_NONE ) { FParse::Value( *Switches[ PrereqNameIdx ], TEXT( "PrereqName=" ), PrereqName ); } if( PrereqPathIdx != INDEX_NONE ) { FParse::Value( *Switches[ PrereqPathIdx ], TEXT( "PrereqPath=" ), PrereqPath ); } if( PrereqArgsIdx != INDEX_NONE ) { FParse::Value( *Switches[ PrereqArgsIdx ], TEXT( "PrereqArgs=" ), PrereqArgs ); } if (ManifestsListIdx != INDEX_NONE) { bool bShouldStopOnComma = false; FParse::Value(*Switches[ManifestsListIdx], TEXT("ManifestsList="), ManifestsList, bShouldStopOnComma); } else if (ManifestsFileIdx != INDEX_NONE) { FParse::Value( *Switches[ ManifestsFileIdx ], TEXT( "ManifestsFile=" ), ManifestsFile); } FString CustomValue; FString Left; FString Right; for (const auto& Switch : Switches) { if (FParse::Value(*Switch, TEXT("custom="), CustomValue)) { if (CustomValue.Split(TEXT("="), &Left, &Right)) { Left.Trim(); Left.TrimTrailing(); Right.Trim(); Right.TrimTrailing(); CustomFields.Add(Left, FVariant(Right)); } } else if (FParse::Value(*Switch, TEXT("customfloat="), CustomValue)) { if (CustomValue.Split(TEXT("="), &Left, &Right)) { Left.Trim(); Left.TrimTrailing(); Right.Trim(); Right.TrimTrailing(); if (!Right.IsNumeric()) { GLog->Log(ELogVerbosity::Error, TEXT("An error occurred processing token -customfloat. Non Numeric character found right of =")); bSuccess = false; } CustomFields.Add(Left, FVariant(TCString<TCHAR>::Atod(*Right))); } } else if (FParse::Value(*Switch, TEXT("customint="), CustomValue)) { if (CustomValue.Split(TEXT("="), &Left, &Right)) { Left.Trim(); Left.TrimTrailing(); Right.Trim(); Right.TrimTrailing(); if (!Right.IsNumeric()) { GLog->Log(ELogVerbosity::Error, TEXT("An error occurred processing token -customint. Non Numeric character found right of =")); bSuccess = false; } CustomFields.Add(Left, FVariant(TCString<TCHAR>::Atoi64(*Right))); } } } FPaths::NormalizeDirectoryName( RootDirectory ); FPaths::NormalizeDirectoryName( CloudDirectory ); if (bSuccess) { // Initialize the configuration system, we can only do this reliably if we have CloudDirectory (i.e. bSuccess is true) IniFile = CloudDirectory / TEXT("BuildPatchTool.ini"); GConfig->InitializeConfigSystem(); } if (DataAgeThresholdIdx != INDEX_NONE) { FParse::Value(*Switches[DataAgeThresholdIdx], TEXT("DataAgeThreshold="), DataAgeThreshold); } else if (bSuccess && bCompactify) { // For compactification, if we don't pass in DataAgeThreshold, and it's not in BuildPatchTool.ini, // then we set it to zero, to indicate that any unused chunks are valid for deletion if (!GConfig->GetFloat(TEXT("Compactify"), TEXT("DataAgeThreshold"), DataAgeThreshold, IniFile)) { GLog->Log(ELogVerbosity::Warning, TEXT("DataAgeThreshold not supplied, so all unreferenced data is eliglble for deletion. Note that this process is NOT compatible with any concurrently running patch generaiton processes")); DataAgeThreshold = 0.0f; } } else if (bSuccess && bPatchGeneration) { // For patch generation, if we don't pass in DataAgeThreshold, and it's not specified in BuildPatchTool.ini, // then we set bChunkWithReuseAgeThreshold to false, which indicates that *all* patch data is valid for reuse if (!GConfig->GetFloat(TEXT("PatchGeneration"), TEXT("DataAgeThreshold"), DataAgeThreshold, IniFile)) { GLog->Log(ELogVerbosity::Warning, TEXT("DataAgeThreshold not supplied, so all existing data is eligible for reuse. Note that this process is NOT compatible with any concurrently running compactify processes")); DataAgeThreshold = 0.0f; bPatchWithReuseAgeThreshold = false; } } } // Check for argument error if( !bSuccess ) { GLog->Log(ELogVerbosity::Error, TEXT("An error occurred processing arguments")); return 1; } // Initialize the file manager IFileManager::Get().ProcessCommandLineOptions(); // Load the BuildPatchServices Module TSharedPtr<IBuildPatchServicesModule> BuildPatchServicesModule = StaticCastSharedPtr<IBuildPatchServicesModule>( FModuleManager::Get().LoadModule( TEXT( "BuildPatchServices" ) ) ); // Initialise the UObject system and process our uobject classes FModuleManager::Get().LoadModule(TEXT("CoreUObject")); FCoreDelegates::OnInit.Broadcast(); ProcessNewlyLoadedUObjects(); // Setup the module BuildPatchServicesModule->SetCloudDirectory( CloudDirectory + TEXT( "/" ) ); if (bCompactify) { // Split out our manifests to keep arg (if any) into an array of manifest filenames TArray<FString> ManifestsArr; if (ManifestsList.Len() > 0) { ManifestsList.ParseIntoArray(&ManifestsArr, TEXT(","), true); } else if (ManifestsFile.Len() > 0) { FString ManifestsFilePath = CloudDirectory / ManifestsFile; FString Temp; if (FFileHelper::LoadFileToString(Temp, *ManifestsFilePath)) { Temp.ReplaceInline(TEXT("\r"), TEXT("\n")); Temp.ParseIntoArray(&ManifestsArr, TEXT("\n"), true); } else { GLog->Log(ELogVerbosity::Error, TEXT("Could not open specified manifests to keep file")); BuildPatchServicesModule.Reset(); FCoreDelegates::OnExit.Broadcast(); return 2; } } // Run the compactify routine bSuccess = BuildPatchServicesModule->CompactifyCloudDirectory(ManifestsArr, DataAgeThreshold, bPreview); } else if (bPatchGeneration) { FBuildPatchSettings Settings; Settings.RootDirectory = RootDirectory + TEXT("/"); Settings.AppID = AppID; Settings.AppName = AppName; Settings.BuildVersion = BuildVersion; Settings.LaunchExe = LaunchExe; Settings.LaunchCommand = LaunchCommand; Settings.IgnoreListFile = IgnoreListFile; Settings.PrereqName = PrereqName; Settings.PrereqPath = PrereqPath; Settings.PrereqArgs = PrereqArgs; Settings.DataAgeThreshold = DataAgeThreshold; Settings.bShouldHonorReuseThreshold = bPatchWithReuseAgeThreshold; Settings.CustomFields = CustomFields; // Run the build generation if (FParse::Param(FCommandLine::Get(), TEXT("nochunks"))) { bSuccess = BuildPatchServicesModule->GenerateFilesManifestFromDirectory(Settings); } else { bSuccess = BuildPatchServicesModule->GenerateChunksManifestFromDirectory(Settings); } } else { GLog->Log(ELogVerbosity::Error, TEXT("Unknown tool mode")); BuildPatchServicesModule.Reset(); FCoreDelegates::OnExit.Broadcast(); return 3; } // Release the module ptr BuildPatchServicesModule.Reset(); // Check for processing error if (!bSuccess) { GLog->Log(ELogVerbosity::Error, TEXT("A fatal error occurred executing BuildPatchTool.exe")); FCoreDelegates::OnExit.Broadcast(); return 4; } FCoreDelegates::OnExit.Broadcast(); GLog->Log(TEXT("BuildPatchToolMain completed successfuly")); return 0; }
int RunSlateViewer( const TCHAR* CommandLine ) { // start up the main loop GEngineLoop.PreInit(CommandLine); // Make sure all UObject classes are registered and default properties have been initialized ProcessNewlyLoadedUObjects(); // Tell the module manager is may now process newly-loaded UObjects when new C++ modules are loaded FModuleManager::Get().StartProcessingNewlyLoadedObjects(); // crank up a normal Slate application using the platform's standalone renderer FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer()); // Load the source code access module ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>( FName( "SourceCodeAccess" ) ); // Manually load in the source code access plugins, as standalone programs don't currently support plugins. #if PLATFORM_MAC IModuleInterface& XCodeSourceCodeAccessModule = FModuleManager::LoadModuleChecked<IModuleInterface>( FName( "XCodeSourceCodeAccess" ) ); SourceCodeAccessModule.SetAccessor(FName("XCodeSourceCodeAccess")); #elif PLATFORM_WINDOWS IModuleInterface& VisualStudioSourceCodeAccessModule = FModuleManager::LoadModuleChecked<IModuleInterface>( FName( "VisualStudioSourceCodeAccess" ) ); SourceCodeAccessModule.SetAccessor(FName("VisualStudioSourceCodeAccess")); #endif // set the application name FGlobalTabmanager::Get()->SetApplicationTitle(LOCTEXT("AppTitle", "Slate Viewer")); FModuleManager::LoadModuleChecked<ISlateReflectorModule>("SlateReflector").RegisterTabSpawner(WorkspaceMenu::DeveloperMenu); FGlobalTabmanager::Get()->RegisterNomadTabSpawner("WebBrowserTab", FOnSpawnTab::CreateStatic(&SpawnWebBrowserTab)) .SetDisplayName(LOCTEXT("WebBrowserTab", "Web Browser")); if (FParse::Param(FCommandLine::Get(), TEXT("perftest"))) { // Bring up perf test SummonPerfTestSuite(); } else { // Bring up the test suite. RestoreSlateTestSuite(); } #if WITH_SHARED_POINTER_TESTS SharedPointerTesting::TestSharedPointer<ESPMode::Fast>(); SharedPointerTesting::TestSharedPointer<ESPMode::ThreadSafe>(); #endif // loop while the server does the rest while (!GIsRequestingExit) { FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread); FStats::AdvanceFrame(false); FTicker::GetCoreTicker().Tick(FApp::GetDeltaTime()); FSlateApplication::Get().PumpMessages(); FSlateApplication::Get().Tick(); FPlatformProcess::Sleep(0); } FModuleManager::Get().UnloadModulesAtShutdown(); FSlateApplication::Shutdown(); return 0; }