1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "ldparser.h"
6
7#include "ldparser.h"
8#include "services/builder/task.h"
9
10#include "common/util/qtcassert.h"
11
12namespace {
13 // opt. drive letter + filename: (2 brackets)
14 const char * const FILE_PATTERN = "(([A-Za-z]:)?[^:]+\\.[^:]+):";
15 // line no. or elf segment + offset (1 bracket)
16 const char * const POSITION_PATTERN = "(\\S+|\\(\\..+?[+-]0x[a-fA-F0-9]+\\)):";
17 const char * const COMMAND_PATTERN = "^(.*[\\\\/])?([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-)?(ld|gold)(-[0-9\\.]+)?(\\.exe)?: ";
18 const char *const RANLIB_PATTERN = "ranlib(.exe)?: (file: (.*) has no symbols)$";
19}
20
21const char TASK_CATEGORY_COMPILE[] = "Task.Category.Compile";
22
23LdParser::LdParser()
24{
25 setObjectName(QLatin1String("LdParser"));
26 ranlib.setPattern(QLatin1String(RANLIB_PATTERN));
27 QTC_CHECK(ranlib.isValid());
28 regExpLinker.setPattern(QLatin1Char('^') +
29 QString::fromLatin1(FILE_PATTERN) + QLatin1Char('(') +
30 QString::fromLatin1(FILE_PATTERN) + QLatin1String(")?(") +
31 QLatin1String(POSITION_PATTERN) + QLatin1String(")?\\s(.+)$"));
32 QTC_CHECK(regExpLinker.isValid());
33
34 regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN));
35 QTC_CHECK(regExpGccNames.isValid());
36}
37
38void LdParser::stdError(const QString &line)
39{
40 QString lne = rightTrimmed(line);
41 if (lne.startsWith(QLatin1String("TeamBuilder "))
42 || lne.startsWith(QLatin1String("distcc["))
43 || lne.contains(QLatin1String("ar: creating "))) {
44 IOutputParser::stdError(line);
45 return;
46 }
47
48 if (lne.startsWith(QLatin1String("collect2:"))) {
49 Task task = Task(Task::Error,
50 lne /* description */,
51 Utils::FileName() /* filename */,
52 -1 /* linenumber */,
53 TASK_CATEGORY_COMPILE);
54 emit addTask(task, 1);
55 return;
56 }
57
58 QRegularExpressionMatch match = ranlib.match(lne);
59 if (match.hasMatch()) {
60 QString description = match.captured(2);
61 Task task(Task::Warning, description,
62 Utils::FileName(), -1,
63 TASK_CATEGORY_COMPILE);
64 emit addTask(task, 1);
65 return;
66 }
67
68 match = regExpGccNames.match(lne);
69 if (match.hasMatch()) {
70 QString description = lne.mid(match.capturedLength());
71 Task::TaskType type = Task::Error;
72 if (description.startsWith(QLatin1String("warning: "))) {
73 type = Task::Warning;
74 description = description.mid(9);
75 } else if (description.startsWith(QLatin1String("fatal: "))) {
76 description = description.mid(7);
77 }
78 Task task(type, description, Utils::FileName() /* filename */, -1 /* line */,
79 TASK_CATEGORY_COMPILE);
80 emit addTask(task, 1);
81 return;
82 }
83
84 match = regExpLinker.match(lne);
85 if (match.hasMatch()) {
86 bool ok;
87 int lineno = match.captured(7).toInt(&ok);
88 if (!ok)
89 lineno = -1;
90 Utils::FileName filename = Utils::FileName::fromUserInput(match.captured(1));
91 const QString sourceFileName = match.captured(4);
92 if (!sourceFileName.isEmpty()
93 && !sourceFileName.startsWith(QLatin1String("(.text"))
94 && !sourceFileName.startsWith(QLatin1String("(.data"))) {
95 filename = Utils::FileName::fromUserInput(sourceFileName);
96 }
97 QString description = match.captured(8).trimmed();
98 Task::TaskType type = Task::Error;
99 if (description.startsWith(QLatin1String("At global scope")) ||
100 description.startsWith(QLatin1String("At top level")) ||
101 description.startsWith(QLatin1String("instantiated from ")) ||
102 description.startsWith(QLatin1String("In ")) ||
103 description.startsWith(QLatin1String("first defined here")) ||
104 description.startsWith(QLatin1String("note:"), Qt::CaseInsensitive)) {
105 type = Task::Unknown;
106 } else if (description.startsWith(QLatin1String("warning: "), Qt::CaseInsensitive)) {
107 type = Task::Warning;
108 description = description.mid(9);
109 }
110 Task task(type, description, filename, lineno, TASK_CATEGORY_COMPILE);
111 emit addTask(task, 1);
112 return;
113 }
114
115 IOutputParser::stdError(line);
116}
117