// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Text.RegularExpressions; using EpicGames.Core; using Microsoft.Extensions.Logging; namespace UnrealBuildTool.Matchers { /// /// Matcher for linker errors /// class LinkEventMatcher : ILogEventMatcher { static readonly Regex s_undefinedReferencePattern = new Regex( @": undefined reference to |undefined symbol|^\s*(ld|ld.lld|lld-link|(.*clang)):|^[^:]*[^a-zA-Z][a-z]?ld: |^\s*>>>"); static readonly Regex s_undefinedSymbolsForArchPattern = new Regex( @"^(\s*)Undefined symbols for architecture"); static readonly Regex s_ldClangPattern = new Regex( @"^\s*(ld|ld.lld|lld-link|clang):"); static readonly Regex s_ldClangErrorPattern = new Regex( @"^\s*(ld|ld.lld|lld-link):\s+error:\s+(?.+)$"); static readonly Regex s_ldMultiplyDefinedPattern = new Regex( @"(?error): L\d+: symbol `(?[^`]+)' multiply defined$"); static readonly Regex s_ldMultiplyDefinedInfoPattern = new Regex( @"error: \.\.\."); static readonly Regex s_microsoftErrorPattern = new Regex( @"error (?LNK\d+):"); static readonly Regex s_ignoredLinkWarningPattern = new Regex( @"^\s*ld: warning: Linker asked to preserve internal global:"); /// public LogEventMatch? Match(ILogCursor cursor) { if (cursor.IsMatch(s_ignoredLinkWarningPattern)) { LogEventBuilder builder = new LogEventBuilder(cursor); return builder.ToMatch(LogEventPriority.High, LogLevel.Information, KnownLogEvents.Linker); } int lineCount = 0; bool isError = false; bool isWarning = false; while (cursor.IsMatch(lineCount, s_undefinedReferencePattern)) { isError |= cursor.Contains(lineCount, "error:"); isWarning |= cursor.Contains(lineCount, "warning:"); lineCount++; } if (lineCount > 0) { LogEventBuilder builder = new LogEventBuilder(cursor); bool hasSymbol = AddSymbolMarkupForLine(builder); for (int idx = 1; idx < lineCount; idx++) { builder.MoveNext(); hasSymbol |= AddSymbolMarkupForLine(builder); } for (; ; ) { if (builder.Next.Contains("ld:")) { break; } else if (builder.Next.Contains("error:")) { isError = true; } else if (builder.Next.Contains("warning:")) { isWarning = true; } else { break; } hasSymbol |= AddSymbolMarkupForLine(builder); builder.MoveNext(); } LogLevel level = (isError || !isWarning) ? LogLevel.Error : LogLevel.Warning; EventId eventId = hasSymbol ? KnownLogEvents.Linker_UndefinedSymbol : KnownLogEvents.Linker; return builder.ToMatch(LogEventPriority.Normal, level, eventId); } Match? match; if (cursor.TryMatch(s_undefinedSymbolsForArchPattern, out match)) { LogEventBuilder builder = new LogEventBuilder(cursor); AddSymbolMarkupForLine(builder); string prefix = $"^(?{match.Groups[1].Value}\\s+)"; while (builder.Next.TryMatch(new Regex(prefix + @"""(?[^""]+)"""), out match)) { string nextPrefix = $"^{match.Groups["prefix"].Value}\\s+"; builder.MoveNext(); builder.AnnotateSymbol(match.Groups["symbol"]); while (builder.Next.TryMatch(new Regex(nextPrefix + "(?[^ ].*) in "), out match)) { builder.MoveNext(); builder.AnnotateSymbol(match.Groups["symbol"]); } } while (builder.Next.IsMatch(s_ldClangPattern)) { builder.MoveNext(); } return builder.ToMatch(LogEventPriority.Normal, LogLevel.Error, KnownLogEvents.Linker_UndefinedSymbol); } if (cursor.TryMatch(s_microsoftErrorPattern, out match)) { LogEventBuilder builder = new LogEventBuilder(cursor); AddSymbolMarkupForLine(builder); Group codeGroup = match.Groups["code"]; builder.Annotate(codeGroup, LogEventMarkup.ErrorCode); if (codeGroup.Value.Equals("LNK2001", StringComparison.Ordinal) || codeGroup.Value.Equals("LNK2019", StringComparison.Ordinal)) { return builder.ToMatch(LogEventPriority.High, LogLevel.Error, KnownLogEvents.Linker_UndefinedSymbol); } else if (codeGroup.Value.Equals("LNK2005", StringComparison.Ordinal) || codeGroup.Value.Equals("LNK4022", StringComparison.Ordinal)) { return builder.ToMatch(LogEventPriority.High, LogLevel.Error, KnownLogEvents.Linker_DuplicateSymbol); } else { return builder.ToMatch(LogEventPriority.High, LogLevel.Error, KnownLogEvents.Linker); } } if (cursor.TryMatch(s_ldMultiplyDefinedPattern, out match)) { LogEventBuilder builder = new LogEventBuilder(cursor); builder.Annotate(match.Groups["severity"], LogEventMarkup.Severity); builder.AnnotateSymbol(match.Groups["symbol"]); while (builder.Current.IsMatch(1, s_ldMultiplyDefinedInfoPattern)) { builder.MoveNext(); } return builder.ToMatch(LogEventPriority.Highest, LogLevel.Error, KnownLogEvents.Linker_DuplicateSymbol); } if (cursor.TryMatch(s_ldClangErrorPattern, out match)) { LogEventBuilder builder = new LogEventBuilder(cursor); builder.Annotate(match.Groups["error"], LogEventMarkup.Message); return builder.ToMatch(LogEventPriority.Highest, LogLevel.Error, KnownLogEvents.Linker); } return null; } static bool AddSymbolMarkupForLine(LogEventBuilder builder) { bool hasSymbols = false; string? message = builder.Current.CurrentLine; if (message == null) { return false; } // Mac link error: // Undefined symbols for architecture arm64: // "Foo::Bar() const", referenced from: Match symbolMatch = Regex.Match(message, "^ \"(?.+)\""); if (symbolMatch.Success) { builder.AnnotateSymbol(symbolMatch.Groups[1]); hasSymbols = true; } // Android link error: // Foo.o:(.data.rel.ro + 0x5d88): undefined reference to `Foo::Bar()' Match undefinedReference = Regex.Match(message, ": undefined reference to [`'](?[^`']+)"); if (undefinedReference.Success) { builder.AnnotateSymbol(undefinedReference.Groups[1]); hasSymbols = true; } // LLD link error: // ld.lld.exe: error: undefined symbol: Foo::Bar() const Match lldMatch = Regex.Match(message, "error: undefined symbol:\\s*(?.+)"); if (lldMatch.Success) { builder.AnnotateSymbol(lldMatch.Groups[1]); hasSymbols = true; } // Link error: // Link: error: L0039: reference to undefined symbol `Foo::Bar() const' in file Match linkMatch = Regex.Match(message, ": reference to undefined symbol [`'](?[^`']+)"); if (linkMatch.Success) { builder.AnnotateSymbol(linkMatch.Groups[1]); hasSymbols = true; } Match linkMultipleMatch = Regex.Match(message, @": (?[^\s]+) already defined in"); if (linkMultipleMatch.Success) { builder.AnnotateSymbol(linkMultipleMatch.Groups[1]); hasSymbols = true; } // Microsoft linker error: // Foo.cpp.obj : error LNK2001: unresolved external symbol \"private: virtual void __cdecl UAssetManager::InitializeAssetBundlesFromMetadata_Recursive(class UStruct const *,void const *,struct FAssetBundleData &,class FName,class TSet,class FDefaultSetAllocator> &)const \" (?InitializeAssetBundlesFromMetadata_Recursive@UAssetManager@@EEBAXPEBVUStruct@@PEBXAEAUFAssetBundleData@@VFName@@AEAV?$TSet@PEBXU?$DefaultKeyFuncs@PEBX$0A@@@VFDefaultSetAllocator@@@@@Z)", Match microsoftMatch = Regex.Match(message, " symbol \"(?[^\"]*)\""); if (microsoftMatch.Success) { builder.AnnotateSymbol(microsoftMatch.Groups[1]); hasSymbols = true; } return hasSymbols; } } }