You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb Per.Larsson #jira UE-170657, UE-160396 #rnx #preflight 637c9232fa348e8480bdc7e2 - When the rehydration functionality was added it started to look like more functionality would be added to the tool than originally thought so I started to add the new functionality via a command system. This change now moves the older legacy functionality to the coommand system as well. - Added a new FVirtualizeCommand which can accept package paths, directory paths, packagelist files or changelists as the input. -- Unlike the legacy commands, virtualizing via changelist does not require the client spec to be provided on the commandline, although doing so will avoid several perforce commands and speed up the call. It is expected that people calling the tool on the commandline will probably opt to not supply a clientspec and let the tool workout which workspace a changelist is under, where as calls from other tools (such as P4VUtils) can provide it if already known to speed things up. -- FVirtualizeLegacyChangeListCommand replicates the functionality of the old -Mode=Changelist command. -- FVirtualizeLegacyPackageListCommand replicates the functionality of the old -Mode=Packagelist command. -- The new command will only try to submit the results if a changelist was provided as the input and even then it will not do so by default. The tool now requires people to opt into submitting the changelist via the command line option -submit. NOTE: Other versions of the command maybe allow submission in the future but the virtualization process needs to be improved so that it can check out package files before this really makes sense. - The rehydration command no longer requires a clientspec on the command line, it wasn't using the value anyway. - Moved the source control code from the app code files to CommandBase. If we add more commands in the future we might want to factor this out to its own base class so commands can opt into source control functionality. - In the future we should probably move the package -> project sorting code (FUnrealVirtualizationToolApp::TrySortFilesByProject) into the command base code as well. [CL 23233456 by paul chipchase in ue5-main branch]
343 lines
9.8 KiB
C++
343 lines
9.8 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Commands/VirtualizeCommand.h"
|
|
|
|
#include "HAL/FileManager.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Misc/ScopeExit.h"
|
|
#include "ProjectFiles.h"
|
|
#include "UnrealVirtualizationTool.h"
|
|
#include "Virtualization/VirtualizationSystem.h"
|
|
|
|
|
|
#include "ISourceControlModule.h"
|
|
#include "ISourceControlProvider.h"
|
|
#include "SourceControlOperations.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
/** Utility */
|
|
TArray<FString> BuildFinalTagDescriptions(TArray<FText>& DescriptionTags)
|
|
{
|
|
TArray<FString> CleanedDescriptions;
|
|
CleanedDescriptions.Reserve(DescriptionTags.Num());
|
|
|
|
for (const FText& Tag : DescriptionTags)
|
|
{
|
|
CleanedDescriptions.AddUnique(Tag.ToString());
|
|
}
|
|
|
|
return CleanedDescriptions;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace UE::Virtualization
|
|
{
|
|
|
|
FVirtualizeCommand::FVirtualizeCommand(FStringView CommandName)
|
|
: FCommand(CommandName)
|
|
{
|
|
|
|
}
|
|
|
|
void FVirtualizeCommand::PrintCmdLineHelp()
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT(""));
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("-Mode=Virtualize -Changelist=<number> -Submit [optional]"));
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("-Mode=Virtualize -Path=<string>"));
|
|
}
|
|
|
|
bool FVirtualizeCommand::Initialize(const TCHAR* CmdLine)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualizeCommand::Initialize);
|
|
|
|
TArray<FString> Tokens;
|
|
TArray<FString> Switches;
|
|
|
|
ParseCommandLine(CmdLine, Tokens, Switches);
|
|
|
|
FString SwitchValue;
|
|
for (const FString& Switch : Switches)
|
|
{
|
|
EPathResult Result = ParseSwitchForPaths(Switch, AllPackages);
|
|
if (Result == EPathResult::Error)
|
|
{
|
|
return false;
|
|
}
|
|
else if (Result == EPathResult::Success)
|
|
{
|
|
continue; // If we already matched the switch we don't need to check against any others
|
|
}
|
|
|
|
if (FParse::Value(*Switch, TEXT("Changelist="), SwitchValue))
|
|
{
|
|
SourceChangelistNumber = SwitchValue;
|
|
}
|
|
else if (Switch == TEXT("Submit"))
|
|
{
|
|
bShouldSubmitChangelist = true;
|
|
}
|
|
}
|
|
|
|
// Process the provided changelist if one was found
|
|
if (!SourceChangelistNumber.IsEmpty() )
|
|
{
|
|
// If no client spec was provided we need to find it for the changelist
|
|
// In theory this duplicates a lot of the work found in ::TryParseChangelist
|
|
// but at the moment the FGetChangelistDetails operation is not compatible
|
|
// with the FSourceControlChangelistStateRef/FSourceControlStateRef API
|
|
// so we are stuck with duplication of work.
|
|
if (ClientSpecName.IsEmpty())
|
|
{
|
|
ClientSpecName = FindClientSpecForChangelist(SourceChangelistNumber);
|
|
if (!ClientSpecName.IsEmpty())
|
|
{
|
|
FSourceControlResultInfo Info;
|
|
if (SCCProvider->SwitchWorkspace(ClientSpecName, Info, nullptr) != ECommandResult::Succeeded)
|
|
{
|
|
UE_LOG(LogVirtualization, Error, TEXT("Failed to switch to workspace '%s'"), *ClientSpecName);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogVirtualization, Error, TEXT("Count not find a valid workspace for the changelist '%s'"), *SourceChangelistNumber);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!TryParseChangelist(ClientSpecName, SourceChangelistNumber, AllPackages, &SourceChangelist))
|
|
{
|
|
UE_LOG(LogVirtualization, Error, TEXT("Failed to find the files in the changelist '%s'"), *SourceChangelistNumber);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FVirtualizeCommand::Run(const TArray<FProject>& Projects)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualizeCommand::Run);
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("Running the virtualization process..."));
|
|
|
|
TArray<FText> DescriptionTags;
|
|
|
|
for (const FProject& Project : Projects)
|
|
{
|
|
TStringBuilder<128> ProjectName;
|
|
ProjectName << Project.GetProjectName();
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("\tChecking package(s) for the project '%s'..."), ProjectName.ToString());
|
|
|
|
FConfigFile EngineConfigWithProject;
|
|
if (!Project.TryLoadConfig(EngineConfigWithProject))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Project.RegisterMountPoints();
|
|
|
|
UE::Virtualization::FInitParams InitParams(ProjectName, EngineConfigWithProject);
|
|
UE::Virtualization::Initialize(InitParams, UE::Virtualization::EInitializationFlags::ForceInitialize);
|
|
|
|
TArray<FString> ProjectPackages = Project.GetAllPackages();
|
|
|
|
TArray<FText> Errors;
|
|
UE::Virtualization::IVirtualizationSystem::Get().TryVirtualizePackages(ProjectPackages, DescriptionTags, Errors);
|
|
|
|
if (!Errors.IsEmpty())
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("The virtualization process failed with the following errors:"));
|
|
for (const FText& Error : Errors)
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("\t%s"), *Error.ToString());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("\tCheck complete"));
|
|
|
|
UE::Virtualization::Shutdown();
|
|
Project.UnRegisterMountPoints();
|
|
}
|
|
|
|
if (bShouldSubmitChangelist)
|
|
{
|
|
TArray<FString> FinalDescriptionTags = BuildFinalTagDescriptions(DescriptionTags);
|
|
|
|
if (!TrySubmitChangelist(SourceChangelist, FinalDescriptionTags))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const TArray<FString>& FVirtualizeCommand::GetPackages() const
|
|
{
|
|
return AllPackages;
|
|
}
|
|
|
|
bool FVirtualizeCommand::TrySubmitChangelist(const FSourceControlChangelistPtr& ChangelistToSubmit, const TArray<FString>& DescriptionTags)
|
|
{
|
|
if (!ChangelistToSubmit.IsValid())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!TryConnectToSourceControl(ClientSpecName))
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Submit failed, cannot find a valid source control provider"));
|
|
return false;
|
|
}
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualizeCommand::TrySubmitChangelist);
|
|
|
|
FSourceControlChangelistRef Changelist = ChangelistToSubmit.ToSharedRef();
|
|
FSourceControlChangelistStatePtr ChangelistState = SCCProvider->GetState(Changelist, EStateCacheUsage::Use);
|
|
|
|
if (!ChangelistState.IsValid())
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Submit failed, failed to find the state for the changelist"));
|
|
return false;
|
|
}
|
|
|
|
const FString ChangelistNumber = ChangelistState->GetDisplayText().ToString();
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("Attempting to submit the changelist '%s'"), *ChangelistNumber);
|
|
|
|
TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>();
|
|
|
|
// Grab the original changelist description then append our tags afterwards
|
|
TStringBuilder<512> Description;
|
|
Description << ChangelistState->GetDescriptionText().ToString();
|
|
|
|
for (const FString& Tag : DescriptionTags)
|
|
{
|
|
Description << TEXT("\n") << Tag;
|
|
}
|
|
|
|
CheckInOperation->SetDescription(FText::FromString(Description.ToString()));
|
|
|
|
if (SCCProvider->Execute(CheckInOperation, Changelist) == ECommandResult::Succeeded)
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("%s"), *CheckInOperation->GetSuccessMessage().ToString());
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Even when log suppression is active we still show errors to the users and as the source control
|
|
// operation should have logged the problem as an error the user will see it. This means we don't
|
|
// have to extract it from CheckInOperation .
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Submit failed, please check the log!"));
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool FVirtualizeCommand::TryParsePackageList(const FString& PackageListPath, TArray<FString>& OutPackages)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualizeCommand::TryParsePackageList);
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("Parsing the package list '%s'..."), *PackageListPath);
|
|
|
|
if (!IFileManager::Get().FileExists(*PackageListPath))
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("\tThe package list '%s' does not exist"), *PackageListPath);
|
|
return false;
|
|
}
|
|
|
|
if (FFileHelper::LoadFileToStringArray(OutPackages, *PackageListPath))
|
|
{
|
|
// We don't have control over how the package list was generated so make sure that the paths
|
|
// are in the format that we want.
|
|
for (FString& PackagePath : OutPackages)
|
|
{
|
|
FPaths::NormalizeFilename(PackagePath);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("\tFailed to parse the package list '%s'"), *PackageListPath);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
FVirtualizeLegacyChangeListCommand::FVirtualizeLegacyChangeListCommand(FStringView CommandName)
|
|
: FVirtualizeCommand(CommandName)
|
|
{
|
|
|
|
}
|
|
|
|
bool FVirtualizeLegacyChangeListCommand::Initialize(const TCHAR* CmdLine)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualizeLegacyChangeListCommand::Initialize);
|
|
|
|
UE_LOG(LogVirtualizationTool, Warning, TEXT("Using legacy -Mode=Changelist command, use '-Mode=Virtualization -Changelist=123' instead!"));
|
|
|
|
if (!FParse::Value(CmdLine, TEXT("-ClientSpecName="), ClientSpecName))
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to find cmdline switch 'ClientSpecName', this is a required parameter!"));
|
|
return false;
|
|
}
|
|
|
|
if (!FParse::Value(CmdLine, TEXT("-Changelist="), SourceChangelistNumber))
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to find cmdline switch 'Changelist', this is a required parameter!"));
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!TryParseChangelist(ClientSpecName, SourceChangelistNumber, AllPackages, &SourceChangelist))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (FParse::Param(CmdLine, TEXT("NoSubmit")))
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("Cmdline parameter '-NoSubmit' found, the changelist will be virtualized but not submitted!"));
|
|
bShouldSubmitChangelist = false;
|
|
}
|
|
else
|
|
{
|
|
// The legacy command was opt out when it came to submitting the changelist, we need to maintain those defaults
|
|
bShouldSubmitChangelist = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
FVirtualizeLegacyPackageListCommand::FVirtualizeLegacyPackageListCommand(FStringView CommandName)
|
|
: FVirtualizeCommand(CommandName)
|
|
{
|
|
|
|
}
|
|
|
|
bool FVirtualizeLegacyPackageListCommand::Initialize(const TCHAR* CmdLine)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualizeLegacyPackageListCommand::Initialize);
|
|
|
|
UE_LOG(LogVirtualizationTool, Warning, TEXT("Using legacy -Mode=Packagelist command, use '-Mode=Virtualization -Path=PathToFile' instead!"));
|
|
|
|
FString PackageListPath;
|
|
if (FParse::Value(CmdLine, TEXT("-Path="), PackageListPath))
|
|
{
|
|
return TryParsePackageList(PackageListPath, AllPackages);
|
|
}
|
|
else
|
|
{
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} // namespace UE::Virtualization
|