1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "cmakeparser.h"
6
7#include "services/builder/fileutils.h"
8#include "common/util/qtcassert.h"
9
10
11const char COMMON_ERROR_PATTERN[] = "^CMake Error at (.*):([0-9]*)( \\((.*)\\))?:";
12const char NEXT_SUBERROR_PATTERN[] = "^CMake Error in (.*):";
13const char LOCATION_LINE_PATTERN[] = ":(\\d+):(?:(\\d+))?$";
14
15const char TASK_CATEGORY_BUILDSYSTEM[] = "Task.Category.Buildsystem";
16
17CMakeParser::CMakeParser()
18{
19 commonError.setPattern(QLatin1String(COMMON_ERROR_PATTERN));
20 commonError.setMinimal(true);
21 QTC_CHECK(commonError.isValid());
22
23 nextSubError.setPattern(QLatin1String(NEXT_SUBERROR_PATTERN));
24 nextSubError.setMinimal(true);
25 QTC_CHECK(nextSubError.isValid());
26
27 locationLine.setPattern(QLatin1String(LOCATION_LINE_PATTERN));
28 QTC_CHECK(locationLine.isValid());
29}
30
31void CMakeParser::stdOutput(const QString &line, OutputPane::OutputFormat format)
32{
33 emit outputAdded(line, format);
34 IOutputParser::stdOutput(line, format);
35}
36
37void CMakeParser::stdError(const QString &line)
38{
39 QString trimmedLine = rightTrimmed(line);
40
41 switch (expectTripleLineErrorData) {
42 case NONE:
43 if (trimmedLine.isEmpty() && !lastTask.isNull()) {
44 if (skippedFirstEmptyLine)
45 doFlush();
46 else
47 skippedFirstEmptyLine = true;
48 return;
49 }
50 if (skippedFirstEmptyLine)
51 skippedFirstEmptyLine = false;
52
53 if (commonError.indexIn(trimmedLine) != -1) {
54 lastTask = Task(Task::Error, QString(), Utils::FileName::fromUserInput(commonError.cap(1)),
55 commonError.cap(2).toInt(), TASK_CATEGORY_BUILDSYSTEM);
56 lines = 1;
57 return;
58 } else if (nextSubError.indexIn(trimmedLine) != -1) {
59 lastTask = Task(Task::Error, QString(), Utils::FileName::fromUserInput(nextSubError.cap(1)), -1,
60 TASK_CATEGORY_BUILDSYSTEM);
61 lines = 1;
62 return;
63 } else if (trimmedLine.startsWith(QLatin1String(" ")) && !lastTask.isNull()) {
64 if (!lastTask.description.isEmpty())
65 lastTask.description.append(QLatin1Char(' '));
66 lastTask.description.append(trimmedLine.trimmed());
67 ++lines;
68 return;
69 } else if (trimmedLine.endsWith(QLatin1String("in cmake code at"))) {
70 expectTripleLineErrorData = LINE_LOCATION;
71 doFlush();
72 lastTask = Task(trimmedLine.contains(QLatin1String("Error")) ? Task::Error : Task::Warning,
73 QString(), Utils::FileName(), -1, TASK_CATEGORY_BUILDSYSTEM);
74 return;
75 } else if (trimmedLine.startsWith("CMake Error: ")) {
76 lastTask = Task(Task::Error, trimmedLine.mid(13),
77 Utils::FileName(), -1, TASK_CATEGORY_BUILDSYSTEM);
78 lines = 1;
79 return;
80 }
81 IOutputParser::stdError(line);
82 return;
83 case LINE_LOCATION:
84 {
85 QRegularExpressionMatch m = locationLine.match(trimmedLine);
86 QTC_CHECK(m.hasMatch());
87 lastTask.file = Utils::FileName::fromUserInput(trimmedLine.mid(0, m.capturedStart()));
88 lastTask.line = m.captured(1).toInt();
89 expectTripleLineErrorData = LINE_DESCRIPTION;
90 }
91 return;
92 case LINE_DESCRIPTION:
93 lastTask.description = trimmedLine;
94 if (trimmedLine.endsWith(QLatin1Char('\"')))
95 expectTripleLineErrorData = LINE_DESCRIPTION2;
96 else {
97 expectTripleLineErrorData = NONE;
98 doFlush();
99 }
100 return;
101 case LINE_DESCRIPTION2:
102 lastTask.description.append(QLatin1Char('\n'));
103 lastTask.description.append(trimmedLine);
104 expectTripleLineErrorData = NONE;
105 doFlush();
106 return;
107 }
108}
109
110void CMakeParser::doFlush()
111{
112 if (lastTask.isNull())
113 return;
114 Task t = lastTask;
115 lastTask.clear();
116 emit addTask(t, lines, 1);
117 lines = 0;
118}
119