1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "gccparser.h"
6
7#include "ldparser.h"
8#include "services/builder/task.h"
9
10#include "common/util/qtcassert.h"
11
12#include <QTextLayout>
13
14// opt. drive letter + filename: (2 brackets)
15static const char FILE_PATTERN[] = "(<command[ -]line>|([A-Za-z]:)?[^:]+):";
16static const char COMMAND_PATTERN[] = "^(.*?[\\\\/])?([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-)?(gcc|g\\+\\+)(-[0-9\\.]+)?(\\.exe)?: ";
17
18const char TASK_CATEGORY_COMPILE[] = "Task.Category.Compile";
19
20GccParser::GccParser()
21{
22 setObjectName(QLatin1String("GCCParser"));
23 regExp.setPattern(QLatin1Char('^') + QLatin1String(FILE_PATTERN)
24 + QLatin1String("(\\d+):(\\d+:)?\\s+((fatal |#)?(warning|error|note):?\\s)?([^\\s].+)$"));
25 QTC_CHECK(regExp.isValid());
26
27 regExpIncluded.setPattern(QString::fromLatin1("\\bfrom\\s") + QLatin1String(FILE_PATTERN)
28 + QLatin1String("(\\d+)(:\\d+)?[,:]?$"));
29 QTC_CHECK(regExpIncluded.isValid());
30
31 // optional path with trailing slash
32 // optional arm-linux-none-thingy
33 // name of executable
34 // optional trailing version number
35 // optional .exe postfix
36 regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN));
37 QTC_CHECK(regExpGccNames.isValid());
38
39 appendOutputParser(new LdParser);
40}
41
42void GccParser::stdError(const QString &line)
43{
44 QString lne = rightTrimmed(line);
45
46 // Blacklist some lines to not handle them:
47 if (lne.startsWith(QLatin1String("TeamBuilder ")) ||
48 lne.startsWith(QLatin1String("distcc["))) {
49 IOutputParser::stdError(line);
50 return;
51 }
52
53 // Handle misc issues:
54 if (lne.startsWith(QLatin1String("ERROR:")) ||
55 lne == QLatin1String("* cpp failed")) {
56 newTask(Task(Task::Error,
57 lne /* description */,
58 Utils::FileName() /* filename */,
59 -1 /* linenumber */,
60 TASK_CATEGORY_COMPILE));
61 return;
62 }
63
64 QRegularExpressionMatch match = regExpGccNames.match(lne);
65 if (match.hasMatch()) {
66 QString description = lne.mid(match.capturedLength());
67 Task::TaskType type = Task::Error;
68 if (description.startsWith(QLatin1String("warning: "))) {
69 type = Task::Warning;
70 description = description.mid(9);
71 } else if (description.startsWith(QLatin1String("fatal: "))) {
72 description = description.mid(7);
73 }
74 Task task(type, description, Utils::FileName(), /* filename */
75 -1, /* line */ TASK_CATEGORY_COMPILE);
76 newTask(task);
77 return;
78 }
79
80 match = regExp.match(lne);
81 if (match.hasMatch()) {
82 Utils::FileName filename = Utils::FileName::fromUserInput(match.captured(1));
83 int lineno = match.captured(3).toInt();
84 Task::TaskType type = Task::Unknown;
85 QString description = match.captured(8);
86 if (match.captured(7) == QLatin1String("warning"))
87 type = Task::Warning;
88 else if (match.captured(7) == QLatin1String("error") ||
89 description.startsWith(QLatin1String("undefined reference to")) ||
90 description.startsWith(QLatin1String("multiple definition of")))
91 type = Task::Error;
92 // Prepend "#warning" or "#error" if that triggered the match on (warning|error)
93 // We want those to show how the warning was triggered
94 if (match.captured(5).startsWith(QLatin1Char('#')))
95 description = match.captured(5) + description;
96
97 Task task(type, description, filename, lineno, TASK_CATEGORY_COMPILE);
98 newTask(task);
99 return;
100 }
101
102 match = regExpIncluded.match(lne);
103 if (match.hasMatch()) {
104 newTask(Task(Task::Unknown,
105 lne.trimmed() /* description */,
106 Utils::FileName::fromUserInput(match.captured(1)) /* filename */,
107 match.captured(3).toInt() /* linenumber */,
108 TASK_CATEGORY_COMPILE));
109 return;
110 } else if (lne.startsWith(QLatin1Char(' '))) {
111 amendDescription(lne, true);
112 return;
113 }
114
115 doFlush();
116 IOutputParser::stdError(line);
117}
118
119void GccParser::stdOutput(const QString &line, OutputPane::OutputFormat format)
120{
121 doFlush();
122 IOutputParser::stdOutput(line, format);
123}
124
125QString GccParser::id()
126{
127 return "OutputParser.Gcc";
128}
129
130void GccParser::newTask(const Task &task)
131{
132 doFlush();
133 currentTask = task;
134 lines = 1;
135}
136
137void GccParser::doFlush()
138{
139 if (currentTask.isNull())
140 return;
141 Task t = currentTask;
142 currentTask.clear();
143 emit addTask(t, lines, 1);
144 lines = 0;
145}
146
147void GccParser::amendDescription(const QString &desc, bool monospaced)
148{
149 if (currentTask.isNull())
150 return;
151 int start = currentTask.description.count() + 1;
152 currentTask.description.append(QLatin1Char('\n'));
153 currentTask.description.append(desc);
154 if (monospaced) {
155 QTextLayout::FormatRange fr;
156 fr.start = start;
157 fr.length = desc.count() + 1;
158 fr.format.setFontStyleHint(QFont::Monospace);
159 }
160 ++lines;
161 return;
162}
163