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-169626 #rnx #preflight 639c4112012902cb8db43e13 - This allows us to provide the user with more ways to customize the virtualization and return more detailed info about it if the calling code wishes to log additional info. In both cases we can extend the options and the data returned without changing the API. - Previously if we virtualized a package that was not checked out in revision control we would warn the user and then skip updating the package file on disk. This means the payloads would be uploaded but the user would be left with no local changes. Since sometimes we know we don't need to check out any package (virtualizing the packages in a change list for example) we don't want to always incur the cost of polling reivision control to see which packages do need checking out. This is why we now allow the caller to request package files be checked out via the new options enum EVirtualizationOptions. -- If the EVirtualizationOptions::Checkout flag is provided we will poll the revision control status of all package files and then check out those which need it. -- We still check if packages can be modified and warn the user if they can't, as package files could be locked in other ways. - Added a new utility function to SourceControlUtilties to make it easier to check out packages. There is similar functionality elsewhere in the code base but the virtualization module is too low level to make use of it. - Updated existing code that calls ::TryVirtualizePackages and add cases of ''using namespace UE::Virtualization' where required to improve readability. - The UnrealVirtualizationTool now supports a new cmdline option "-checkout" that can be used when virtualizing packages. This will checkout any package that was actually virtualized so the result can be saved back out to the workspace domain. This means we no longer require the caller to have checked out the packages before running the tool. [CL 23536832 by paul chipchase in ue5-main branch]
360 lines
10 KiB
C++
360 lines
10 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;
|
|
}
|
|
else if (Switch == TEXT("Checkout"))
|
|
{
|
|
bShouldCheckout = 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();
|
|
|
|
ON_SCOPE_EXIT
|
|
{
|
|
Project.UnRegisterMountPoints();
|
|
};
|
|
|
|
UE::Virtualization::FInitParams InitParams(ProjectName, EngineConfigWithProject);
|
|
UE::Virtualization::Initialize(InitParams, UE::Virtualization::EInitializationFlags::ForceInitialize);
|
|
|
|
ON_SCOPE_EXIT
|
|
{
|
|
UE::Virtualization::Shutdown();
|
|
};
|
|
|
|
TArray<FString> ProjectPackages = Project.GetAllPackages();
|
|
|
|
EVirtualizationOptions Options = EVirtualizationOptions::None;
|
|
if(bShouldCheckout)
|
|
{
|
|
Options |= EVirtualizationOptions::Checkout;
|
|
}
|
|
|
|
FVirtualizationResult ResultInfo;
|
|
UE::Virtualization::IVirtualizationSystem::Get().TryVirtualizePackages(ProjectPackages, Options, ResultInfo);
|
|
|
|
if (!ResultInfo.Errors.IsEmpty())
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("The virtualization process failed with the following errors:"));
|
|
for (const FText& Error : ResultInfo.Errors)
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("\t%s"), *Error.ToString());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("\tCheck complete"));
|
|
}
|
|
|
|
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
|