EM_Task/CoreUObject/Private/UObject/LinkerDiff.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

741 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UObject/LinkerDiff.h"
#include "Algo/Transform.h"
#include "Containers/StringFwd.h"
#include "Misc/OutputDevice.h"
#include "Misc/StringBuilder.h"
#include "UObject/Linker.h"
#include "UObject/LinkerSave.h"
#include "UObject/Package.h"
FLinkerDiff FLinkerDiff::CompareLinkers(FLinker* LHSLinker, FLinker* RHSLinker)
{
FLinkerDiff Diff;
Diff.Generate(LHSLinker, RHSLinker);
return Diff;
}
COREUOBJECT_API bool FLinkerDiff::HasDiffs() const
{
return SummaryDiffs.Num() > 0 || NameMapDiffs.Num() > 0 || GatherableTextDataDiffs.Num() > 0 || ImportDiffs.Num() > 0 || ExportDiffs.Num() > 0 || SoftPackageReferenceDiffs.Num() > 0 || SearchableNameDiffs.Num() > 0;
}
void FLinkerDiff::PrintDiff(FOutputDevice& Ar)
{
auto PrintDiffSection = [&Ar](const FString& HeaderName, const TArray<FString>& DiffSection)
{
if (DiffSection.Num() > 0)
{
Ar.Logf(TEXT("%s: %d"), *HeaderName, DiffSection.Num());
for (const FString& Diff: DiffSection)
{
Ar.Logf(TEXT("\t%s"), *Diff);
}
}
};
if (HasDiffs())
{
Ar.Logf(TEXT("Save (Old vs New) Linker Comparison for: %s"), *PackageName);
PrintDiffSection(TEXT("Summary Diff"), SummaryDiffs);
PrintDiffSection(TEXT("NameMap Diff"), NameMapDiffs);
PrintDiffSection(TEXT("GatherableTextData Diff"), GatherableTextDataDiffs);
PrintDiffSection(TEXT("ImportMap Diff"), ImportDiffs);
PrintDiffSection(TEXT("ExportMap and DependsMap Diffs Diff"), ExportDiffs);
PrintDiffSection(TEXT("SoftPackageReference Diff"), SoftPackageReferenceDiffs);
PrintDiffSection(TEXT("SearchableNames Diff"), SearchableNameDiffs);
}
}
#define COMPARE_MEMBER(LHSRef, RHSRef, Member) \
if (LHSRef.Member != RHSRef.Member) \
{ \
TStringBuilder<256> Builder; \
Builder.Append(TEXT(#Member)); \
Builder.Append(TEXT(": ")); \
Builder.Append(LexToString(LHSRef.Member)); \
Builder.Append(TEXT(" vs ")); \
Builder.Append(LexToString(RHSRef.Member)); \
Diffs.Add(Builder.ToString()); \
}
void FLinkerDiff::Generate(FLinker* LHSLinker, FLinker* RHSLinker)
{
check(LHSLinker->LinkerRoot == RHSLinker->LinkerRoot);
PackageName = LHSLinker->LinkerRoot->GetName();
GenerateSummaryDiff(LHSLinker, RHSLinker);
GenerateNameMapDiff(LHSLinker, RHSLinker);
GenerateGatherableTextDataMapDiff(LHSLinker, RHSLinker);
GenerateImportMapDiff(LHSLinker, RHSLinker);
GenerateExportAndDependsMapDiff(LHSLinker, RHSLinker);
GenerateSofPackageReferenceDiff(LHSLinker, RHSLinker);
GenerateSearchableNameMapDiff(LHSLinker, RHSLinker);
}
void FLinkerDiff::GenerateSummaryDiff(FLinker* LHSLinker, FLinker* RHSLinker)
{
TArray<FString>& Diffs = SummaryDiffs;
const FPackageFileSummary& LHSSummary = LHSLinker->Summary;
const FPackageFileSummary& RHSSummary = RHSLinker->Summary;
// Package Tag, file version
COMPARE_MEMBER(LHSSummary, RHSSummary, Tag);
// COMPARE_MEMBER(LHSSummary, RHSSummary, FileVersionUE4);
// COMPARE_MEMBER(LHSSummary, RHSSummary, FileVersionLicenseeUE4);
// CustomVersionContainer
// COMPARE_MEMBER(LHSSummary, RHSSummary, TotalHeaderSize);
COMPARE_MEMBER(LHSSummary, RHSSummary, PackageFlags);
// FolderName
COMPARE_MEMBER(LHSSummary, RHSSummary, NameCount);
// COMPARE_MEMBER(LHSSummary, RHSSummary, NameOffset);
COMPARE_MEMBER(LHSSummary, RHSSummary, LocalizationId);
COMPARE_MEMBER(LHSSummary, RHSSummary, GatherableTextDataCount);
// COMPARE_MEMBER(LHSSummary, RHSSummary, GatherableTextDataOffset);
COMPARE_MEMBER(LHSSummary, RHSSummary, ExportCount);
// COMPARE_MEMBER(LHSSummary, RHSSummary, ExportOffset);
COMPARE_MEMBER(LHSSummary, RHSSummary, ImportCount);
// COMPARE_MEMBER(LHSSummary, RHSSummary, ImportOffset);
// COMPARE_MEMBER(LHSSummary, RHSSummary, DependsOffset);
COMPARE_MEMBER(LHSSummary, RHSSummary, SoftPackageReferencesCount);
// COMPARE_MEMBER(LHSSummary, RHSSummary, SoftPackageReferencesOffset);
// COMPARE_MEMBER(LHSSummary, RHSSummary, SearchableNamesOffset);
// COMPARE_MEMBER(LHSSummary, RHSSummary, ThumbnailTableOffset);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
COMPARE_MEMBER(LHSSummary, RHSSummary, Guid);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#if WITH_EDITORONLY_DATA
COMPARE_MEMBER(LHSSummary, RHSSummary, PersistentGuid);
#endif
// Generations
// EngineVersion
// PackageSource
COMPARE_MEMBER(LHSSummary, RHSSummary, CompressionFlags);
COMPARE_MEMBER(LHSSummary, RHSSummary, bUnversioned);
// COMPARE_MEMBER(LHSSummary, RHSSummary, AssetRegistryDataOffset);
// COMPARE_MEMBER(LHSSummary, RHSSummary, BulkDataStartOffset);
// COMPARE_MEMBER(LHSSummary, RHSSummary, WorldTileInfoDataOffset);
// ChunkIDs
COMPARE_MEMBER(LHSSummary, RHSSummary, PreloadDependencyCount);
// COMPARE_MEMBER(LHSSummary, RHSSummary, PreloadDependencyOffset);
}
void FLinkerDiff::GenerateNameMapDiff(FLinker* LHSLinker, FLinker* RHSLinker)
{
TArray<FString>& Diffs = NameMapDiffs;
const TArray<FNameEntryId>& LHSNameMap = LHSLinker->NameMap;
TSet<FNameEntryId> RHSNameSet(RHSLinker->NameMap);
for (const FNameEntryId& NameEntryId: LHSNameMap)
{
FSetElementId Id = RHSNameSet.FindId(NameEntryId);
if (Id.IsValidId())
{
RHSNameSet.Remove(Id);
}
// We haven't found the name in the RHS set, mark a missing name diff
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS Name: "));
Builder.Append(FName::CreateFromDisplayId(NameEntryId, 0).ToString());
Diffs.Add(Builder.ToString());
}
}
// What's left in the set are RHS diff, mark a new name diff
for (const FNameEntryId& NameEntryId: RHSNameSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS Name: "));
Builder.Append(FName::CreateFromDisplayId(NameEntryId, 0).ToString());
Diffs.Add(Builder.ToString());
}
}
inline uint32 GetTypeHash(const FGatherableTextData& TextData)
{
return GetTypeHash(TextData.SourceData.SourceString);
}
bool operator==(const FGatherableTextData& LHS, const FGatherableTextData& RHS)
{
return LHS.NamespaceName == RHS.NamespaceName &&
LHS.SourceData.SourceString == RHS.SourceData.SourceString &&
LHS.SourceData.SourceStringMetaData.IsExactMatch(RHS.SourceData.SourceStringMetaData) &&
LHS.SourceSiteContexts.Num() == RHS.SourceSiteContexts.Num();
}
void FLinkerDiff::GenerateGatherableTextDataMapDiff(FLinker* LHSLinker, FLinker* RHSLinker)
{
TArray<FString>& Diffs = GatherableTextDataDiffs;
//@todo: Might need to check source site contexts
const TArray<FGatherableTextData>& LHSGatherableTextDataMap = LHSLinker->GatherableTextDataMap;
TSet<FGatherableTextData> RHSGatherableTextDataSet(RHSLinker->GatherableTextDataMap);
for (const FGatherableTextData& TextData: LHSGatherableTextDataMap)
{
FSetElementId Id = RHSGatherableTextDataSet.FindId(TextData);
if (Id.IsValidId())
{
RHSGatherableTextDataSet.Remove(Id);
}
// We haven't found the name in the RHS set, mark a missing text data diff
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS Gatherable Text: "));
Builder.Append(TextData.SourceData.SourceString);
Diffs.Add(Builder.ToString());
}
}
for (const FGatherableTextData& TextData: RHSGatherableTextDataSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS Text Data: "));
Builder.Append(TextData.SourceData.SourceString);
Diffs.Add(Builder.ToString());
}
}
struct FLinkerImportObject
{
FLinkerImportObject(FObjectImport& InImport, FLinker* AssociatedLinker)
: Import(InImport), Linker(AssociatedLinker)
{}
FObjectImport& Import;
FLinker* Linker;
};
inline uint32 GetTypeHash(const FLinkerImportObject& Import)
{
return HashCombine(GetTypeHash(Import.Import.ObjectName), GetTypeHash(Import.Linker->GetPathName(Import.Import.OuterIndex)));
}
bool operator==(const FLinkerImportObject& LHS, const FLinkerImportObject& RHS)
{
return LHS.Import.ObjectName == RHS.Import.ObjectName &&
LHS.Linker->GetPathName(LHS.Import.OuterIndex) == RHS.Linker->GetPathName(RHS.Import.OuterIndex) &&
LHS.Import.ClassPackage == RHS.Import.ClassPackage &&
LHS.Import.ClassName == RHS.Import.ClassName &&
LHS.Import.GetPackageName() == RHS.Import.GetPackageName() &&
LHS.Import.XObject == RHS.Import.XObject;
}
void FLinkerDiff::GenerateImportMapDiff(FLinker* LHSLinker, FLinker* RHSLinker)
{
TArray<FString>& Diffs = ImportDiffs;
TArray<FObjectImport>& LHSImportMap = LHSLinker->ImportMap;
TSet<FLinkerImportObject> RHSImportSet;
for (FObjectImport& Import: RHSLinker->ImportMap)
{
RHSImportSet.Add(FLinkerImportObject(Import, RHSLinker));
}
for (FObjectImport& Import: LHSImportMap)
{
FSetElementId Id = RHSImportSet.FindId(FLinkerImportObject(Import, LHSLinker));
if (Id.IsValidId())
{
RHSImportSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing or different RHS Import: "));
Builder.Append(Import.ObjectName.ToString());
Builder.Append(TEXT(", Outer Path: "));
Builder.Append(LHSLinker->GetPathName(Import.OuterIndex));
Diffs.Add(Builder.ToString());
}
}
for (const FLinkerImportObject& Import: RHSImportSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New or different RHS Import: "));
Builder.Append(Import.Import.ObjectName.ToString());
Builder.Append(TEXT(", Outer Path: "));
Builder.Append(RHSLinker->GetPathName(Import.Import.OuterIndex));
Diffs.Add(Builder.ToString());
}
}
struct FLinkerExportObject
{
FLinkerExportObject(FObjectExport& InExport, FLinker* AssociatedLinker, int32 InExportIndex)
: Export(InExport), Linker(AssociatedLinker), ExportIndex(InExportIndex)
{}
FObjectExport& Export;
FLinker* Linker;
int32 ExportIndex;
};
inline uint32 GetTypeHash(const FLinkerExportObject& Export)
{
return HashCombine(GetTypeHash(Export.Export.ObjectName), GetTypeHash(Export.Linker->GetPathName(Export.Export.OuterIndex)));
}
bool operator==(const FLinkerExportObject& LHS, const FLinkerExportObject& RHS)
{
return LHS.Export.ObjectName == RHS.Export.ObjectName &&
LHS.Linker->GetPathName(LHS.Export.OuterIndex) == RHS.Linker->GetPathName(RHS.Export.OuterIndex) &&
LHS.Linker->GetClassName(LHS.Export.ClassIndex) == RHS.Linker->GetClassName(RHS.Export.ClassIndex) &&
LHS.Linker->GetPathName(LHS.Export.SuperIndex) == RHS.Linker->GetPathName(RHS.Export.SuperIndex) &&
LHS.Linker->GetPathName(LHS.Export.TemplateIndex) == RHS.Linker->GetPathName(RHS.Export.TemplateIndex);
}
void FLinkerDiff::GenerateExportAndDependsMapDiff(FLinker* LHSLinker, FLinker* RHSLinker)
{
TArray<FString>& Diffs = ExportDiffs;
TArray<FObjectExport>& LHSExportMap = LHSLinker->ExportMap;
TSet<FLinkerExportObject> RHSExportSet;
for (int32 RHSIndex = 0; RHSIndex < RHSLinker->ExportMap.Num(); ++RHSIndex)
{
RHSExportSet.Add(FLinkerExportObject(RHSLinker->ExportMap[RHSIndex], RHSLinker, RHSIndex));
}
for (int32 LHSIndex = 0; LHSIndex < LHSLinker->ExportMap.Num(); ++LHSIndex)
{
FObjectExport& Export = LHSLinker->ExportMap[LHSIndex];
FSetElementId Id = RHSExportSet.FindId(FLinkerExportObject(Export, LHSLinker, LHSIndex));
if (Id.IsValidId())
{
GenerateExportDiff(LHSLinker, FLinkerExportObject(Export, LHSLinker, LHSIndex), RHSExportSet[Id]);
GenerateDependsArrayDiff(LHSLinker, LHSIndex, RHSLinker, RHSExportSet[Id].ExportIndex);
RHSExportSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing or different RHS Export: "));
Builder.Append(Export.ObjectName.ToString());
Builder.Append(TEXT(", Outer Path: "));
Builder.Append(LHSLinker->GetPathName(Export.OuterIndex));
Diffs.Add(Builder.ToString());
}
}
for (const FLinkerExportObject& Export: RHSExportSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New or different RHS Export: "));
Builder.Append(Export.Export.ObjectName.ToString());
Builder.Append(TEXT(", Outer Path: "));
Builder.Append(RHSLinker->GetPathName(Export.Export.OuterIndex));
Diffs.Add(Builder.ToString());
}
}
void FLinkerDiff::GenerateSofPackageReferenceDiff(FLinker* LHSLinker, FLinker* RHSLinker)
{
TArray<FString>& Diffs = SoftPackageReferenceDiffs;
const TArray<FName>& LHSSoftPackageReferenceList = LHSLinker->SoftPackageReferenceList;
TSet<FName> RHSSoftPackageReferenceSet(RHSLinker->SoftPackageReferenceList);
for (const FName& SoftName: LHSSoftPackageReferenceList)
{
FSetElementId Id = RHSSoftPackageReferenceSet.FindId(SoftName);
if (Id.IsValidId())
{
RHSSoftPackageReferenceSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS Soft Package Reference: "));
Builder.Append(SoftName.ToString());
Diffs.Add(Builder.ToString());
}
}
for (const FName& SoftName: RHSSoftPackageReferenceSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS Soft Package Reference: "));
Builder.Append(SoftName.ToString());
Diffs.Add(Builder.ToString());
}
}
FPackageIndex FindResourcePackageIndex(const FObjectResource& LHSObject, bool bIsImport, FLinker* LHSLinker, FLinker* RHSLinker)
{
if (bIsImport)
{
for (int32 Index = 0; Index < RHSLinker->ImportMap.Num(); ++Index)
{
FObjectResource& Import = RHSLinker->ImportMap[Index];
if (LHSObject.ObjectName == Import.ObjectName &&
LHSLinker->GetPathName(LHSObject.OuterIndex) == RHSLinker->GetPathName(Import.OuterIndex))
{
return FPackageIndex::FromImport(Index);
}
}
}
else
{
for (int32 Index = 0; Index < RHSLinker->ExportMap.Num(); ++Index)
{
FObjectResource& Export = RHSLinker->ExportMap[Index];
if (LHSObject.ObjectName == Export.ObjectName &&
LHSLinker->GetPathName(LHSObject.OuterIndex) == RHSLinker->GetPathName(Export.OuterIndex))
{
return FPackageIndex::FromExport(Index);
}
}
}
return FPackageIndex();
}
void FLinkerDiff::GenerateSearchableNameMapDiff(FLinker* LHSLinker, FLinker* RHSLinker)
{
TArray<FString>& Diffs = SearchableNameDiffs;
const TMap<FPackageIndex, TArray<FName>>& LHSSearchableNamesMap = LHSLinker->SearchableNamesMap;
const TMap<FPackageIndex, TArray<FName>>& RHSSearchableNamesMap = RHSLinker->SearchableNamesMap;
for (const auto& SearchableNamePair: LHSSearchableNamesMap)
{
check(!SearchableNamePair.Key.IsNull());
FPackageIndex RHSIndex = FindResourcePackageIndex(LHSLinker->ImpExp(SearchableNamePair.Key), SearchableNamePair.Key.IsImport(), LHSLinker, RHSLinker);
if (!RHSIndex.IsNull() && RHSLinker->SearchableNamesMap.Contains(RHSIndex))
{
GenerateSearchableNameArrayDiff(SearchableNamePair.Value, RHSLinker->SearchableNamesMap[RHSIndex]);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS Searchable Name Map entry for: "));
Builder.Append(LHSLinker->GetPathName(SearchableNamePair.Key));
Diffs.Add(Builder.ToString());
}
}
for (const auto& SearchableNamePair: RHSSearchableNamesMap)
{
FPackageIndex LHSIndex = FindResourcePackageIndex(RHSLinker->ImpExp(SearchableNamePair.Key), SearchableNamePair.Key.IsImport(), RHSLinker, LHSLinker);
if (LHSIndex.IsNull())
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS Searchable Name Map entry for: "));
Builder.Append(RHSLinker->GetPathName(SearchableNamePair.Key));
Diffs.Add(Builder.ToString());
}
}
}
FString LexToString(EObjectFlags Flags)
{
return LexToString((int32)Flags);
}
void FLinkerDiff::GenerateExportDiff(FLinker* LHSLinker, const FLinkerExportObject& LHSExport, const FLinkerExportObject& RHSExport)
{
TArray<FString> Diffs;
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, ObjectFlags);
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, SerialSize);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, PackageGuid);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, PackageFlags);
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, SerializationBeforeSerializationDependencies);
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, CreateBeforeSerializationDependencies);
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, SerializationBeforeCreateDependencies);
COMPARE_MEMBER(LHSExport.Export, RHSExport.Export, CreateBeforeCreateDependencies);
if (Diffs.Num() > 0)
{
ExportDiffs.Add(FString::Printf(TEXT("Export Diffs for resource: %s, Outer: %s"), *LHSExport.Export.ObjectName.ToString(), *LHSLinker->GetPathName(LHSExport.Export.OuterIndex)));
ExportDiffs.Append(MoveTemp(Diffs));
}
}
void FLinkerDiff::GenerateDependsArrayDiff(FLinker* LHSLinker, int32 LHSIndex, FLinker* RHSLinker, int32 RHSIndex)
{
TArray<FString>& Diffs = ExportDiffs;
TArray<FString> LHSExportDependencies;
Algo::Transform(LHSLinker->DependsMap[LHSIndex], LHSExportDependencies, [LHSLinker](FPackageIndex Index)
{
return LHSLinker->GetPathName(Index);
});
TSet<FString> RHSExportDependencies;
Algo::Transform(RHSLinker->DependsMap[RHSIndex], RHSExportDependencies, [RHSLinker](FPackageIndex Index)
{
return RHSLinker->GetPathName(Index);
});
TSet<FString> MissingDependencyDiffsToValidate;
for (const FString& PathName: LHSExportDependencies)
{
FSetElementId Id = RHSExportDependencies.FindId(PathName);
if (Id.IsValidId())
{
RHSExportDependencies.Remove(Id);
}
else
{
MissingDependencyDiffsToValidate.Add(PathName);
}
}
// if we have missing dependencies, validate they are accurate, the old save mechanism was adding indirect references as dependencies, which we should be able to trim
if (MissingDependencyDiffsToValidate.Num())
{
// @todo: no need for now
}
FString ResourcePathName = RHSLinker->GetPathName(FPackageIndex::FromExport(RHSIndex));
for (const FString& PathName: MissingDependencyDiffsToValidate)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS Dependency "));
Builder.Append(PathName);
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
for (const FString& PathName: RHSExportDependencies)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS Dependency "));
Builder.Append(PathName);
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
// Validate preload dependencies
FObjectExport& LHSExport = LHSLinker->ExportMap[LHSIndex];
FObjectExport& RHSExport = RHSLinker->ExportMap[RHSIndex];
if (LHSExport.FirstExportDependency != -1 && RHSExport.FirstExportDependency != -1)
{
TArray<FPackageIndex>& LHSDepList = static_cast<FLinkerSave*>(LHSLinker)->DepListForErrorChecking;
TArray<FPackageIndex>& RHSDepList = static_cast<FLinkerSave*>(RHSLinker)->DepListForErrorChecking;
TSet<FPackageIndex> RHSDepSet;
int32 LHSStartIndex = 0, RHSStartIndex = 0;
RHSStartIndex = RHSExport.FirstExportDependency;
LHSStartIndex = LHSExport.FirstExportDependency;
if (LHSExport.SerializationBeforeSerializationDependencies != RHSExport.SerializationBeforeSerializationDependencies)
{
for (int32 Index = 0; Index < RHSExport.SerializationBeforeSerializationDependencies; ++Index)
{
RHSDepSet.Add(RHSDepList[RHSStartIndex + Index]);
}
for (int32 Index = 0; Index < LHSExport.SerializationBeforeSerializationDependencies; ++Index)
{
FPackageIndex LHSDepIndex = LHSDepList[LHSStartIndex + Index];
FPackageIndex RHSDepIndex = FindResourcePackageIndex(LHSLinker->ImpExp(LHSDepIndex), LHSDepIndex.IsImport(), LHSLinker, RHSLinker);
FSetElementId Id = RHSDepSet.FindId(RHSDepIndex);
if (Id.IsValidId())
{
RHSDepSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS SerializationBeforeSerializationDependencies Preload Dependencies "));
Builder.Append(LHSLinker->GetPathName(LHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
for (FPackageIndex RHSDepIndex: RHSDepSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS SerializationBeforeSerializationDependencies Preload Dependencies "));
Builder.Append(RHSLinker->GetPathName(RHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
RHSStartIndex += RHSExport.SerializationBeforeSerializationDependencies;
LHSStartIndex += LHSExport.SerializationBeforeSerializationDependencies;
if (LHSExport.CreateBeforeSerializationDependencies != RHSExport.CreateBeforeSerializationDependencies)
{
RHSDepSet.Reset();
for (int32 Index = 0; Index < RHSExport.CreateBeforeSerializationDependencies; ++Index)
{
RHSDepSet.Add(RHSDepList[RHSStartIndex + Index]);
}
for (int32 Index = 0; Index < LHSExport.CreateBeforeSerializationDependencies; ++Index)
{
FPackageIndex LHSDepIndex = LHSDepList[LHSStartIndex + Index];
FPackageIndex RHSDepIndex = FindResourcePackageIndex(LHSLinker->ImpExp(LHSDepIndex), LHSDepIndex.IsImport(), LHSLinker, RHSLinker);
FSetElementId Id = RHSDepSet.FindId(RHSDepIndex);
if (Id.IsValidId())
{
RHSDepSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS CreateBeforeSerializationDependencies Preload Dependencies "));
Builder.Append(LHSLinker->GetPathName(LHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
for (FPackageIndex RHSDepIndex: RHSDepSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS CreateBeforeSerializationDependencies Preload Dependencies "));
Builder.Append(RHSLinker->GetPathName(RHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
RHSStartIndex += RHSExport.CreateBeforeSerializationDependencies;
LHSStartIndex += LHSExport.CreateBeforeSerializationDependencies;
if (LHSExport.SerializationBeforeCreateDependencies != RHSExport.SerializationBeforeCreateDependencies)
{
RHSDepSet.Reset();
for (int32 Index = 0; Index < RHSExport.SerializationBeforeCreateDependencies; ++Index)
{
RHSDepSet.Add(RHSDepList[RHSStartIndex + Index]);
}
for (int32 Index = 0; Index < LHSExport.SerializationBeforeCreateDependencies; ++Index)
{
FPackageIndex LHSDepIndex = LHSDepList[LHSStartIndex + Index];
FPackageIndex RHSDepIndex = FindResourcePackageIndex(LHSLinker->ImpExp(LHSDepIndex), LHSDepIndex.IsImport(), LHSLinker, RHSLinker);
FSetElementId Id = RHSDepSet.FindId(RHSDepIndex);
if (Id.IsValidId())
{
RHSDepSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS SerializationBeforeCreateDependencies Preload Dependencies "));
Builder.Append(LHSLinker->GetPathName(LHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
for (FPackageIndex RHSDepIndex: RHSDepSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS SerializationBeforeCreateDependencies Preload Dependencies "));
Builder.Append(RHSLinker->GetPathName(RHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
RHSStartIndex += RHSExport.SerializationBeforeCreateDependencies;
LHSStartIndex += LHSExport.SerializationBeforeCreateDependencies;
if (LHSExport.CreateBeforeCreateDependencies != RHSExport.CreateBeforeCreateDependencies)
{
RHSDepSet.Reset();
for (int32 Index = 0; Index < RHSExport.CreateBeforeCreateDependencies; ++Index)
{
RHSDepSet.Add(RHSDepList[RHSStartIndex + Index]);
}
for (int32 Index = 0; Index < LHSExport.CreateBeforeCreateDependencies; ++Index)
{
FPackageIndex LHSDepIndex = LHSDepList[LHSStartIndex + Index];
FPackageIndex RHSDepIndex = FindResourcePackageIndex(LHSLinker->ImpExp(LHSDepIndex), LHSDepIndex.IsImport(), LHSLinker, RHSLinker);
FSetElementId Id = RHSDepSet.FindId(RHSDepIndex);
if (Id.IsValidId())
{
RHSDepSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS CreateBeforeCreateDependencies Preload Dependencies "));
Builder.Append(LHSLinker->GetPathName(LHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
for (FPackageIndex RHSDepIndex: RHSDepSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS CreateBeforeCreateDependencies Preload Dependencies "));
Builder.Append(RHSLinker->GetPathName(RHSDepIndex));
Builder.Append(TEXT(" for resource "));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
}
else if (LHSExport.FirstExportDependency != RHSExport.FirstExportDependency)
{
TStringBuilder<1024> Builder;
Builder.Append(RHSExport.FirstExportDependency == -1 ? TEXT("Missing RHS Cooked Preload Dependencies for resource") : TEXT("New RHS Cooked Preload Dependencies for resource"));
Builder.Append(ResourcePathName);
Diffs.Add(Builder.ToString());
}
}
void FLinkerDiff::GenerateSearchableNameArrayDiff(const TArray<FName>& LHSNameArray, const TArray<FName>& RHSNameArray)
{
TArray<FString>& Diffs = SearchableNameDiffs;
TSet<FName> RHSNameSet(RHSNameArray);
for (const FName& Name: LHSNameArray)
{
FSetElementId Id = RHSNameSet.FindId(Name);
if (Id.IsValidId())
{
RHSNameSet.Remove(Id);
}
else
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("Missing RHS searchable name: "));
Builder.Append(Name.ToString());
Diffs.Add(Builder.ToString());
}
}
for (const FName& Name: RHSNameSet)
{
TStringBuilder<1024> Builder;
Builder.Append(TEXT("New RHS searchable name: "));
Builder.Append(Name.ToString());
Diffs.Add(Builder.ToString());
}
}