TSharedRef<FTokenizedMessage> FTokenizedMessage::Create(EMessageSeverity::Type InSeverity, const FText& InMessageText) { TSharedRef<FTokenizedMessage> Message = MakeShareable(new FTokenizedMessage()); Message->SetSeverity( InSeverity ); Message->AddToken( FSeverityToken::Create(InSeverity) ); if(!InMessageText.IsEmpty()) { Message->AddToken( FTextToken::Create(InMessageText) ); } return Message; }
/** Helper function to output more detailed error info if available */ static void OutputErrorDetail(FArchiveUObject* LinkerArchive, const FName& LogName) { if ( GSerializedObject && GSerializedImportLinker ) { FMessageLog LoadErrors(LogName); TSharedRef<FTokenizedMessage> Message = LoadErrors.Info(); Message->AddToken(FTextToken::Create(LOCTEXT("FailedLoad_Message", "Failed to load"))); Message->AddToken(FAssetNameToken::Create(GSerializedImportLinker->GetImportPathName(GSerializedImportIndex))); Message->AddToken(FTextToken::Create(LOCTEXT("FailedLoad_Referenced", "Referenced by"))); Message->AddToken(FUObjectToken::Create(GSerializedObject)); auto SerializedProperty = LinkerArchive ? LinkerArchive->GetSerializedProperty() : nullptr; if (SerializedProperty != nullptr) { FString PropertyPathName = SerializedProperty->GetPathName(); Message->AddToken(FTextToken::Create(LOCTEXT("FailedLoad_Property", "Property"))); Message->AddToken(FAssetNameToken::Create(PropertyPathName, FText::FromString( PropertyPathName) ) ); } } }
static void LogGetPackageLinkerError(FArchiveUObject* LinkerArchive, const TCHAR* InFilename, const FText& InFullErrorMessage, const FText& InSummaryErrorMessage, UObject* InOuter, uint32 LoadFlags) { static FName NAME_LoadErrors("LoadErrors"); struct Local { /** Helper function to output more detailed error info if available */ static void OutputErrorDetail(FArchiveUObject* LinkerArchive, const FName& LogName) { if ( GSerializedObject && GSerializedImportLinker ) { FMessageLog LoadErrors(LogName); TSharedRef<FTokenizedMessage> Message = LoadErrors.Info(); Message->AddToken(FTextToken::Create(LOCTEXT("FailedLoad_Message", "Failed to load"))); Message->AddToken(FAssetNameToken::Create(GSerializedImportLinker->GetImportPathName(GSerializedImportIndex))); Message->AddToken(FTextToken::Create(LOCTEXT("FailedLoad_Referenced", "Referenced by"))); Message->AddToken(FUObjectToken::Create(GSerializedObject)); auto SerializedProperty = LinkerArchive ? LinkerArchive->GetSerializedProperty() : nullptr; if (SerializedProperty != nullptr) { FString PropertyPathName = SerializedProperty->GetPathName(); Message->AddToken(FTextToken::Create(LOCTEXT("FailedLoad_Property", "Property"))); Message->AddToken(FAssetNameToken::Create(PropertyPathName, FText::FromString( PropertyPathName) ) ); } } } }; FMessageLog LoadErrors(NAME_LoadErrors); // Display log error regardless LoadFlag settings SET_WARN_COLOR(COLOR_RED); if (LoadFlags & LOAD_NoWarn) { UE_LOG(LogLinker, Log, TEXT("%s"), *InFullErrorMessage.ToString()); } else { UE_LOG(LogLinker, Warning, TEXT("%s"), *InFullErrorMessage.ToString()); } CLEAR_WARN_COLOR(); if( GIsEditor && !IsRunningCommandlet() ) { // if we don't want to be warned, skip the load warning if (!(LoadFlags & LOAD_NoWarn)) { // we only want to output errors that content creators will be able to make sense of, // so any errors we cant get links out of we will just let be output to the output log (above) // rather than clog up the message log if(InFilename != NULL && InOuter != NULL) { // Output the summary error & the filename link. This might be something like "..\Content\Foo.upk Out of Memory" TSharedRef<FTokenizedMessage> Message = LoadErrors.Error(); Message->AddToken(FAssetNameToken::Create(FPackageName::FilenameToLongPackageName(InFilename))); Message->AddToken(FTextToken::Create(FText::FromString(TEXT(":")))); Message->AddToken(FTextToken::Create(InSummaryErrorMessage)); Message->AddToken(FAssetNameToken::Create(FPackageName::FilenameToLongPackageName(InOuter->GetPathName()))); } Local::OutputErrorDetail(LinkerArchive, NAME_LoadErrors); } } else { if (!(LoadFlags & LOAD_NoWarn)) { Local::OutputErrorDetail(LinkerArchive, NAME_LoadErrors); } FFormatNamedArguments Arguments; Arguments.Add(TEXT("FileName"), FText::FromString(InFilename ? InFilename : InOuter ? *InOuter->GetName() : TEXT("NULL"))); Arguments.Add(TEXT("ErrorMessage"), InFullErrorMessage); const FText Error = FText::Format(LOCTEXT("FailedLoad", "Failed to load '{FileName}': {ErrorMessage}"), Arguments); // @see ResavePackagesCommandlet if( FParse::Param(FCommandLine::Get(),TEXT("SavePackagesThatHaveFailedLoads")) == true ) { LoadErrors.Warning(Error); } else { // Gracefully handle missing packages SafeLoadError( InOuter, LoadFlags, *InFullErrorMessage.ToString(), *Error.ToString() ); } } }
TArray< TSharedRef<FTokenizedMessage> > FCompilerResultsLog::ParseCompilerLogDump(const FString& LogDump) { TArray< TSharedRef<FTokenizedMessage> > Messages; TArray< FString > MessageLines; LogDump.ParseIntoArray(MessageLines, TEXT("\n"), false); // delete any trailing empty lines for (int32 i = MessageLines.Num()-1; i >= 0; --i) { if (!MessageLines[i].IsEmpty()) { if (i < MessageLines.Num() - 1) { MessageLines.RemoveAt(i+1, MessageLines.Num() - (i+1)); } break; } } for (int32 i = 0; i < MessageLines.Num(); ++i) { FString Line = MessageLines[i]; if (Line.EndsWith(TEXT("\r"))) { Line = Line.LeftChop(1); } Line = Line.ConvertTabsToSpaces(4).TrimTrailing(); // handle output line error message if applicable // @todo Handle case where there are parenthesis in path names // @todo Handle errors reported by Clang FString LeftStr, RightStr; FString FullPath, LineNumberString; if (Line.Split(TEXT(")"), &LeftStr, &RightStr, ESearchCase::CaseSensitive) && LeftStr.Split(TEXT("("), &FullPath, &LineNumberString, ESearchCase::CaseSensitive) && LineNumberString.IsNumeric() && (FCString::Strtoi(*LineNumberString, NULL, 10) > 0)) { EMessageSeverity::Type Severity = EMessageSeverity::Error; FString FullPathTrimmed = FullPath; FullPathTrimmed.Trim(); if (FullPathTrimmed.Len() != FullPath.Len()) // check for leading whitespace { Severity = EMessageSeverity::Info; } TSharedRef<FTokenizedMessage> Message = FTokenizedMessage::Create( Severity ); if ( Severity == EMessageSeverity::Info ) // add whitespace now { FString Whitespace = FullPath.Left(FullPath.Len() - FullPathTrimmed.Len()); Message->AddToken( FTextToken::Create( FText::FromString( Whitespace ) ) ); FullPath = FullPathTrimmed; } FString Link = FullPath + TEXT("(") + LineNumberString + TEXT(")"); Message->AddToken( FTextToken::Create( FText::FromString( Link ) )->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&FCompilerResultsLog::OnGotoError) ) ); Message->AddToken( FTextToken::Create( FText::FromString( RightStr ) ) ); Messages.Add(Message); } else { EMessageSeverity::Type Severity = EMessageSeverity::Info; if (Line.Contains(TEXT("error LNK"), ESearchCase::CaseSensitive)) { Severity = EMessageSeverity::Error; } TSharedRef<FTokenizedMessage> Message = FTokenizedMessage::Create( Severity ); Message->AddToken( FTextToken::Create( FText::FromString( Line ) ) ); Messages.Add(Message); } } return Messages; }
/** Create a tokenized message record from a message containing @@ indicating where each UObject* in the ArgPtr list goes and place it in the MessageLog. */ void FCompilerResultsLog::InternalLogMessage(const EMessageSeverity::Type& Severity, const TCHAR* Message, va_list ArgPtr) { UEdGraphNode* OwnerNode = nullptr; // Create the tokenized message TSharedRef<FTokenizedMessage> Line = FTokenizedMessage::Create( Severity ); Messages.Add(Line); const TCHAR* DelimiterStr = TEXT("@@"); int32 DelimLength = FCString::Strlen(DelimiterStr); const TCHAR* Start = Message; if (Start && DelimLength) { while (const TCHAR* At = FCString::Strstr(Start, DelimiterStr)) { // Found a delimiter, create a token from the preceding text Line->AddToken( FTextToken::Create( FText::FromString( FString(At - Start, Start) ) ) ); Start += DelimLength + (At - Start); // And read the object and add another token for the object UObject* ObjectArgument = va_arg(ArgPtr, UObject*); FText ObjText; if (ObjectArgument) { // Remap object references to the source nodes ObjectArgument = FindSourceObject(ObjectArgument); if (ObjectArgument) { UEdGraphNode* Node = Cast<UEdGraphNode>(ObjectArgument); const UEdGraphPin* Pin = (Node? nullptr : Cast<UEdGraphPin>(ObjectArgument)); //Get owner node reference, consider the first if (OwnerNode == nullptr) { OwnerNode = (Pin ? Pin->GetOwningNodeUnchecked() : Node); } if (ObjectArgument->GetOutermost() == GetTransientPackage()) { ObjText = LOCTEXT("Transient", "(transient)"); } else if (Node != NULL) { ObjText = Node->GetNodeTitle(ENodeTitleType::ListView); } else if (Pin != NULL) { ObjText = Pin->GetDisplayName(); } else { ObjText = FText::FromString( ObjectArgument->GetName() ); } } else { ObjText = LOCTEXT("None", "(none)"); } } else { ObjText = LOCTEXT("None", "(none)"); } Line->AddToken( FUObjectToken::Create( ObjectArgument, ObjText ) ); } Line->AddToken( FTextToken::Create( FText::FromString( Start ) ) ); } va_end(ArgPtr); // Register node error/warning. AnnotateNode(OwnerNode, Line); if( !bSilentMode && (!bLogInfoOnly || (Severity == EMessageSeverity::Info)) ) { if(Severity == EMessageSeverity::CriticalError || Severity == EMessageSeverity::Error) { UE_LOG(LogBlueprint, Error, TEXT("[compiler] %s"), *Line->ToText().ToString()); } else if(Severity == EMessageSeverity::Warning || Severity == EMessageSeverity::PerformanceWarning) { UE_LOG(LogBlueprint, Warning, TEXT("[compiler] %s"), *Line->ToText().ToString()); } else { UE_LOG(LogBlueprint, Log, TEXT("[compiler] %s"), *Line->ToText().ToString()); } } }