You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb Sebastian.Nordgren #rnx #jira UE-156436 #preflight 62c287f9a3568e30664eb94f ### VA Standalone Tool - We now plan to add much more functionality to the tool than just virtualizing and submitting changelists, so to make this easier I am moving the tool towards a design where it should be fairly easy to add new functionality. - Added FCommand, which is a base class for adding new functionality, simple derive from FCommand and hook it up at the appropriate locations. -- In the future it should be possible for new command types to automatically register themselves to be initiated from the command line. There should be no need to edit UnrealVirtualizationToolApp to add a new command but this will be done as an additional work item. -- At the moment FCommand comes with a number of utility methods to call that cover some common source control commands. -- The original functionality has not yet been moved to the command system and so the code is a little bit weird at the moment. Updating older code to the new system will be done as an additional work item. - FProject/FPlugin have been moved to their own code files. ### Rehydrate Command - The rehydrate command will take a number of packages, check them out of source control and then attempt to virtualize them. - At the moment the chekout logic is fairly basic, we just check out every package supplied, we don't check if the package is virtualized or not yet. This can be improved in additional work items. Ideally by the end of command the only packages that we have checked out should also be rehydrated. - At the moment the command can either take a path of a specific package, a path of a directory to find packages in, or a changelist containing packages that should be rehydrated. - A cleint spec (workspace) can optionally be provided, but if not supplied we will attempt to find a client spec for which to check out the packages. - Currently we will check out the packages to the default change list. ### Rehydrate process - Added the rehydration process in it's own code files in the virtualization module. Like the virtualization process this is exposed in a public header file and no via the Core interface which means it is very specific to our module/implementation. - The process expects that the caller will have checked out any required packages from source control. It will treat being unable to update a package file as an error. - Added PackageUtils.h/.cpp and moved some of the generic code from the virtualization process code there so that it can be shared by the rehydration process. ### Misc Moving away from the using things like FPackagePath as that requires that the correct mount points have been registered for a project and at the moment (with the flakiness of FConfig*) it seems that the best idea would be to prefer absolute file paths where possible. [CL 20982284 by paul chipchase in ue5-main branch]
193 lines
6.4 KiB
C++
193 lines
6.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CommandBase.h"
|
|
|
|
#include "ISourceControlModule.h"
|
|
#include "ISourceControlProvider.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Misc/ScopeExit.h"
|
|
#include "SourceControlInitSettings.h"
|
|
#include "SourceControlOperations.h"
|
|
#include "UnrealVirtualizationTool.h"
|
|
|
|
namespace UE::Virtualization
|
|
{
|
|
|
|
/** Utility for testing if a file path resolves to a valid package file or not */
|
|
bool IsPackageFile(const FString FilePath)
|
|
{
|
|
// ::IsPackageExtension requires a TCHAR so we cannot use FPathViews here
|
|
const FString Extension = FPaths::GetExtension(FilePath);
|
|
|
|
// Currently we don't virtualize text based assets so no call to FPackageName::IsTextPackageExtension
|
|
return FPackageName::IsPackageExtension(*Extension);
|
|
}
|
|
|
|
bool FCommand::TryConnectToSourceControl(FStringView ClientSpecName)
|
|
{
|
|
if (SCCProvider.IsValid())
|
|
{
|
|
// Already connected so just return
|
|
return true;
|
|
}
|
|
|
|
UE_LOG(LogVirtualizationTool, Log, TEXT("Trying to connect to source control..."));
|
|
|
|
FSourceControlInitSettings SCCSettings(FSourceControlInitSettings::EBehavior::OverrideAll);
|
|
SCCSettings.AddSetting(TEXT("P4Client"), ClientSpecName);
|
|
|
|
SCCProvider = ISourceControlModule::Get().CreateProvider(FName("Perforce"), TEXT("UnrealVirtualizationTool"), SCCSettings);
|
|
if (SCCProvider.IsValid())
|
|
{
|
|
SCCProvider->Init(true);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to create a perforce connection"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool FCommand::TryCheckOutFilesForProject(FStringView ClientSpecName, FStringView ProjectRoot, const TArray<FString>& ProjectPackages)
|
|
{
|
|
// Override the root directory for source control to use the project for which we are trying to hydrate packages for
|
|
ISourceControlModule::Get().RegisterSourceControlProjectDirDelegate(FSourceControlProjectDirDelegate::CreateLambda([&ProjectRoot]()
|
|
{
|
|
return *WriteToString<260>(ProjectRoot, TEXT("/"));
|
|
}));
|
|
|
|
ON_SCOPE_EXIT
|
|
{
|
|
ISourceControlModule::Get().UnregisterSourceControlProjectDirDelegate();
|
|
};
|
|
|
|
if (!TryConnectToSourceControl(ClientSpecName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("\t\tChecking status of package files..."));
|
|
|
|
TArray<TSharedRef<ISourceControlState>> FileStates;
|
|
if (SCCProvider->GetState(ProjectPackages, FileStates, EStateCacheUsage::ForceUpdate) != ECommandResult::Succeeded)
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to find file states for packages from source control"));
|
|
return false;
|
|
}
|
|
|
|
TArray<FString> FilesToCheckout;
|
|
FilesToCheckout.Reserve(FileStates.Num());
|
|
|
|
for (const TSharedRef<ISourceControlState>& State : FileStates)
|
|
{
|
|
if (State->CanCheckout())
|
|
{
|
|
FilesToCheckout.Add(State->GetFilename());
|
|
}
|
|
}
|
|
|
|
if (!FilesToCheckout.IsEmpty())
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("\t\tChecking out %d file(s) from source control..."), FilesToCheckout.Num());
|
|
|
|
if (SCCProvider->Execute(ISourceControlOperation::Create<FCheckOut>(), FilesToCheckout, EConcurrency::Synchronous) != ECommandResult::Succeeded)
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to checkout packages from source control"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("\t\tAll files checked out and writable"));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FCommand::TryParseChangelist(FStringView ClientSpecName, FStringView ChangelistNumber, TArray<FString>& OutPackages)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(TryParseChangelist);
|
|
|
|
UE_LOG(LogVirtualizationTool, Display, TEXT("\tAttempting to parse changelist '%.*s' in workspace '%.*s'"),
|
|
ChangelistNumber.Len(), ChangelistNumber.GetData(),
|
|
ClientSpecName.Len(), ClientSpecName.GetData());
|
|
|
|
if (!TryConnectToSourceControl(ClientSpecName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!SCCProvider.IsValid())
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("No valid source control connection found!"));
|
|
return false;
|
|
}
|
|
|
|
if (!SCCProvider->UsesChangelists())
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("The source control provider does not support the use of changelists"));
|
|
return false;
|
|
}
|
|
|
|
TArray<FSourceControlChangelistRef> Changelists = SCCProvider->GetChangelists(EStateCacheUsage::ForceUpdate);
|
|
if (Changelists.IsEmpty())
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to find any changelists"));
|
|
return false;
|
|
}
|
|
|
|
TArray<FSourceControlChangelistStateRef> ChangelistsStates;
|
|
if (SCCProvider->GetState(Changelists, ChangelistsStates, EStateCacheUsage::Use) != ECommandResult::Succeeded)
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to find changelist data"));
|
|
return false;
|
|
}
|
|
|
|
for (FSourceControlChangelistStateRef& ChangelistState : ChangelistsStates)
|
|
{
|
|
const FText DisplayText = ChangelistState->GetDisplayText();
|
|
|
|
if (ChangelistNumber == DisplayText.ToString())
|
|
{
|
|
TSharedRef<FUpdatePendingChangelistsStatus, ESPMode::ThreadSafe> Operation = ISourceControlOperation::Create<FUpdatePendingChangelistsStatus>();
|
|
|
|
// TODO: Updating only the CL we want does not currently work and even if it did we still end up with a pointless
|
|
// p4 changes command before updating the files. Given we know the changelist number via FSourceControlChangelistRef
|
|
// we should be able to just request the file states be updated.
|
|
// This is also a lot of code to write for a simple "give me all files in a changelist" operation, if we don't add
|
|
// support directly in the API we should move this to a utility namespace in the source control module.
|
|
|
|
FSourceControlChangelistRef Changelist = ChangelistState->GetChangelist();
|
|
Operation->SetChangelistsToUpdate(MakeArrayView(&Changelist, 1));
|
|
Operation->SetUpdateFilesStates(true);
|
|
|
|
if (SCCProvider->Execute(Operation, EConcurrency::Synchronous) != ECommandResult::Succeeded)
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to find the files in changelist '%.*s'"), ChangelistNumber.Len(), ChangelistNumber.GetData());
|
|
return false;
|
|
}
|
|
|
|
const TArray<FSourceControlStateRef>& FilesinChangelist = ChangelistState->GetFilesStates();
|
|
for (const FSourceControlStateRef& FileState : FilesinChangelist)
|
|
{
|
|
if (IsPackageFile(FileState->GetFilename()))
|
|
{
|
|
OutPackages.Add(FileState->GetFilename());
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogVirtualizationTool, Log, TEXT("Ignoring non-package file '%s'"), *FileState->GetFilename());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogVirtualizationTool, Error, TEXT("Failed to find the changelist '%.*s'"), ChangelistNumber.Len(), ChangelistNumber.GetData());
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
} //namespace UE::Virtualization
|