Files
UnrealEngineUWP/Engine/Source/Programs/LiveCodingConsole/Private/LiveCodingManifest.cpp
Ben Marsh 9fb2236941 LiveCoding: Fix asserts/crashes due to variables being reconstructed when files are split out of a unity blob.
These variables (and their dynamic initializers) were being treated as new, rather than being linked back to their original instances. UBT now outputs a mapping of object files to their original unity object file during a live coding compile, which allows Live++ to disambiguate between new and reconstructed instances.

#rb none
#jira UE-72002

[CL 6455178 by Ben Marsh in 4.22 branch]
2019-05-14 18:09:57 -04:00

132 lines
3.5 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "LiveCodingManifest.h"
#include "Misc/FileHelper.h"
bool FLiveCodingManifest::Read(const TCHAR* FileName, FString& OutFailReason)
{
// Read the file to a string
FString FileContents;
if (!FFileHelper::LoadFileToString(FileContents, FileName))
{
OutFailReason = FString::Printf(TEXT("Unable to read from %s"), FileName);
return false;
}
// Deserialize a JSON object from the string
TSharedPtr< FJsonObject > Object;
TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(FileContents);
if ( !FJsonSerializer::Deserialize(Reader, Object) || !Object.IsValid() )
{
OutFailReason = FString::Printf(TEXT("Unable to parse %s"), FileName);
return false;
}
// Parse the data
if (!Parse(*Object, OutFailReason))
{
OutFailReason = FString::Printf(TEXT("Unable to parse %s (%s)"), FileName, *OutFailReason);
return false;
}
return true;
}
bool FLiveCodingManifest::Parse(FJsonObject& Object, FString& OutFailReason)
{
if (!Object.TryGetStringField(TEXT("LinkerPath"), LinkerPath))
{
OutFailReason = TEXT("missing LinkerPath field");
return false;
}
const TSharedPtr<FJsonObject>* EnvironmentObject;
if (Object.TryGetObjectField(TEXT("LinkerEnvironment"), EnvironmentObject))
{
for(const TPair<FString, TSharedPtr<FJsonValue>>& Pair : EnvironmentObject->Get()->Values)
{
const FJsonValue* Value = Pair.Value.Get();
if (Value->Type == EJson::String)
{
LinkerEnvironment.Add(Pair.Key, Value->AsString());
}
}
}
const TArray<TSharedPtr<FJsonValue>>* ModulesArray;
if (!Object.TryGetArrayField(TEXT("Modules"), ModulesArray))
{
OutFailReason = TEXT("missing Modules field");
return false;
}
for (const TSharedPtr<FJsonValue>& ModuleValue : *ModulesArray)
{
if (ModuleValue->Type != EJson::Object)
{
OutFailReason = TEXT("invalid module object");
return false;
}
const FJsonObject& ModuleObject = *ModuleValue->AsObject();
FString OutputFile;
if (!ModuleObject.TryGetStringField(TEXT("Output"), OutputFile))
{
OutFailReason = TEXT("missing module 'Output' field");
return false;
}
const TArray<TSharedPtr<FJsonValue>>* Inputs;
if (!ModuleObject.TryGetArrayField(TEXT("Inputs"), Inputs))
{
OutFailReason = TEXT("missing module 'Inputs' field");
return false;
}
TArray<FString>& InputFiles = BinaryToObjectFiles.Add(OutputFile);
for (const TSharedPtr<FJsonValue>& Input : *Inputs)
{
if (Input->Type != EJson::String)
{
OutFailReason = TEXT("invalid module input field");
return false;
}
InputFiles.Add(Input->AsString());
}
}
const TArray<TSharedPtr<FJsonValue>>* AdaptiveUnityFilesArray;
if (Object.TryGetArrayField(TEXT("AdaptiveUnityFiles"), AdaptiveUnityFilesArray))
{
for (const TSharedPtr<FJsonValue>& AdaptiveUnityFileValue : *AdaptiveUnityFilesArray)
{
if (AdaptiveUnityFileValue->Type != EJson::Object)
{
OutFailReason = TEXT("invalid module object");
return false;
}
const FJsonObject& AdaptiveUnityFileObject = *AdaptiveUnityFileValue->AsObject();
FString ObjectFile;
if (!AdaptiveUnityFileObject.TryGetStringField(TEXT("ObjectFile"), ObjectFile))
{
OutFailReason = TEXT("missing module 'ObjectFile' field");
return false;
}
FString UnityObjectFile;
if (!AdaptiveUnityFileObject.TryGetStringField(TEXT("UnityObjectFile"), UnityObjectFile))
{
OutFailReason = TEXT("missing module 'UnityObjectFile' field");
return false;
}
ObjectFileToUnityObjectFile.Emplace(MoveTemp(ObjectFile), MoveTemp(UnityObjectFile));
}
}
return true;
}