1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "codeporting.h" |
6 | #include "common/util/custompaths.h" |
7 | |
8 | #include <QFile> |
9 | #include <QDebug> |
10 | #include <QRegularExpression> |
11 | #include <QDir> |
12 | |
13 | static QStringList kSrcItemNames{QObject::tr("FilePath" ), QObject::tr("CodeRange" ), |
14 | QObject::tr("Key" ), QObject::tr("Suggestion" ), QObject::tr("FileType" )}; |
15 | static QStringList kLibItemNames{QObject::tr("FileName" ), QObject::tr("Installed" ), QObject::tr("Detail" )}; |
16 | CodePorting::CodePorting(QObject *parent) |
17 | : QObject(parent) |
18 | { |
19 | connect(&process, &QProcess::started, [this]() { |
20 | this->updateStatus(kRuning); |
21 | QString startMsg = tr("Start execute command: \"%1\" \"%2\" in workspace \"%3\".\n" ) |
22 | .arg(process.program().split("/" ).last(), process.arguments().join(" " ), process.workingDirectory()); |
23 | emit outputInformation(startMsg, OutputPane::OutputFormat::NormalMessage); |
24 | }); |
25 | |
26 | connect(&process, &QProcess::readyReadStandardOutput, [&]() { |
27 | process.setReadChannel(QProcess::StandardOutput); |
28 | while (process.canReadLine()) { |
29 | QString line = QString::fromUtf8(process.readLine()); |
30 | auto mode = parseFormat(line); |
31 | emit outputInformation(line, OutputPane::OutputFormat::StdOut, mode); |
32 | } |
33 | }); |
34 | |
35 | connect(&process, &QProcess::readyReadStandardError, [&]() { |
36 | process.setReadChannel(QProcess::StandardError); |
37 | while (process.canReadLine()) { |
38 | QString line = QString::fromUtf8(process.readLine()); |
39 | QRegularExpression reg("\\s\\[INFO\\]\\s" ); |
40 | bool isInfo = reg.match(line).hasMatch(); |
41 | OutputPane::OutputFormat format = isInfo ? OutputPane::StdOut : OutputPane::StdErr; |
42 | auto mode = parseFormat(line); |
43 | emit outputInformation(line, format, mode); |
44 | |
45 | // The output content include report path, so get it. |
46 | QString reportPath = parseReportPath(line); |
47 | if (!reportPath.isEmpty()) { |
48 | bool bSuccessful = parseReportFromFile(reportPath); |
49 | if (bSuccessful) { |
50 | emit outputInformation(tr("Parse report successful.\n" ), OutputPane::StdOut, mode); |
51 | } else { |
52 | emit outputInformation(tr("Parse report Failed.\n" ), OutputPane::StdErr, mode); |
53 | } |
54 | } |
55 | } |
56 | }); |
57 | |
58 | connect(&process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), |
59 | [&](int exitcode, QProcess::ExitStatus exitStatus) { |
60 | QString retMsg; |
61 | OutputPane::OutputFormat format = OutputPane::NormalMessage; |
62 | if (0 == exitcode && exitStatus == QProcess::ExitStatus::NormalExit) { |
63 | retMsg = tr("The process \"%1\" exited normally.\n" ).arg(process.program()); |
64 | this->updateStatus(kSuccessful); |
65 | } else if (exitStatus == QProcess::NormalExit) { |
66 | retMsg = tr("The process \"%1\" exited with code %2.\n" ) |
67 | .arg(process.program(), QString::number(exitcode)); |
68 | this->updateStatus(kSuccessful); |
69 | } else { |
70 | retMsg = tr("The process \"%1\" crashed.\n" ) |
71 | .arg(process.program()); |
72 | format = OutputPane::ErrorMessage; |
73 | this->updateStatus(kFailed); |
74 | } |
75 | emit outputInformation(retMsg, format); |
76 | }); |
77 | } |
78 | |
79 | bool CodePorting::start(const QString &projectSrcPath, const QString &srcCPU, const QString &buildDir, const QString &destCPU) |
80 | { |
81 | if (status == kRuning) |
82 | return false; |
83 | |
84 | // check script and source dirctory is exists. |
85 | QString scriptPath = CustomPaths::global(CustomPaths::Scripts); |
86 | QString portingCli = scriptPath + "/porting-script/code_porting.py" ; |
87 | QDir dir; |
88 | if (!QFile::exists(portingCli) || !dir.exists(projectSrcPath)) |
89 | return false; |
90 | |
91 | projSrcPath = projectSrcPath; |
92 | |
93 | process.setProgram(getPython()); |
94 | QStringList args; |
95 | args.append(portingCli); |
96 | args.append("-S" ); |
97 | args.append(projectSrcPath); |
98 | args.append("-B" ); |
99 | args.append(buildDir); |
100 | args.append("--scpu" ); |
101 | args.append(srcCPU); |
102 | args.append("--dcpu" ); |
103 | args.append(destCPU); |
104 | process.setArguments(args); |
105 | process.start(); |
106 | process.waitForFinished(-1); |
107 | |
108 | return true; |
109 | } |
110 | |
111 | bool CodePorting::abort() |
112 | { |
113 | return true; |
114 | } |
115 | |
116 | CodePorting::PortingStatus CodePorting::getStatus() const |
117 | { |
118 | return status; |
119 | } |
120 | |
121 | bool CodePorting::isRunning() |
122 | { |
123 | return status == kRuning; |
124 | } |
125 | |
126 | const CodePorting::Report &CodePorting::getReport() const |
127 | { |
128 | return report; |
129 | } |
130 | |
131 | const QStringList &CodePorting::getSrcItemNames() const |
132 | { |
133 | return kSrcItemNames; |
134 | } |
135 | |
136 | const QStringList &CodePorting::getLibItemNames() const |
137 | { |
138 | return kLibItemNames; |
139 | } |
140 | |
141 | const QList<QStringList> CodePorting::getSourceReport() const |
142 | { |
143 | return report["cppfiles" ]; |
144 | } |
145 | |
146 | const QList<QStringList> CodePorting::getDependLibReport() const |
147 | { |
148 | return report["sofiles" ]; |
149 | } |
150 | |
151 | /** |
152 | * @brief findAll |
153 | * @param pattern |
154 | * @param str |
155 | * @param Greedy: find all items matched when Greedy is true. |
156 | * @return matched items. |
157 | */ |
158 | QList<QString> findAll(QString pattern, QString str, bool Greedy) |
159 | { |
160 | QRegExp rxlen(pattern); |
161 | rxlen.setMinimal(Greedy); |
162 | int position = 0; |
163 | QList<QString> strList; |
164 | while (position >= 0) { |
165 | position = rxlen.indexIn(str, position); |
166 | if (position < 0) |
167 | break; |
168 | strList << rxlen.cap(1); |
169 | position += rxlen.matchedLength(); |
170 | } |
171 | return strList; |
172 | } |
173 | |
174 | /** |
175 | * @brief CodePorting::getPython |
176 | * get the latest version |
177 | * @return |
178 | */ |
179 | QString CodePorting::getPython() |
180 | { |
181 | if (pythonCmd.isEmpty()) { |
182 | QDir dir("/usr/bin" ); |
183 | QStringList filter { "Python*.*" }; |
184 | dir.setNameFilters(filter); |
185 | QStringList pythonList = dir.entryList(); |
186 | |
187 | QString pattern = "((\\d)|(\\d.\\d))($|\\s)" ; |
188 | QStringList versions = findAll(pattern, pythonList.join(" " ), true); |
189 | |
190 | double newVersion = 0; |
191 | for (auto version : versions) { |
192 | double v = version.toDouble(); |
193 | if (v > newVersion) { |
194 | newVersion = v; |
195 | } |
196 | } |
197 | pythonCmd = "python" + QString::number(newVersion); |
198 | } |
199 | return pythonCmd; |
200 | } |
201 | |
202 | void CodePorting::updateStatus(CodePorting::PortingStatus _status) |
203 | { |
204 | status = _status; |
205 | emit notifyPortingStatus(status); |
206 | } |
207 | |
208 | bool CodePorting::parseReportFromFile(const QString &reportPath) |
209 | { |
210 | bool successful = false; |
211 | |
212 | bool isReportExists = QFile::exists(reportPath); |
213 | qInfo() << "Report exists: " << isReportExists; |
214 | |
215 | if (isReportExists) { |
216 | QFile file(reportPath); |
217 | if (file.open(QIODevice::ReadOnly)) { |
218 | report.clear(); |
219 | const char *quotes = "\"" ; |
220 | while (!file.atEnd()) { |
221 | QString line = file.readLine(); |
222 | QStringList cols = line.split("\",\"" ); |
223 | if (cols.length() == kItemsCount) { |
224 | // remove redundant quotes |
225 | cols.first().remove(quotes); |
226 | cols.last().remove(quotes); |
227 | |
228 | QString type = cols[kFileType].simplified(); |
229 | if (report.find(type) == report.end()) { |
230 | report.insert(type, {cols}); |
231 | } else { |
232 | report[type].push_back(cols); |
233 | } |
234 | } |
235 | } |
236 | file.close(); |
237 | successful = true; |
238 | } |
239 | } |
240 | return successful; |
241 | } |
242 | |
243 | QString CodePorting::parseReportPath(const QString &line) |
244 | { |
245 | QString reportPath; |
246 | QRegularExpression reg("porting advisor for" ); |
247 | auto match = reg.match(line); |
248 | if (match.hasMatch()) { |
249 | reg.setPattern("(?<=\\s:\\s)(.*)" ); |
250 | match = reg.match(line); |
251 | if (match.hasMatch()) { |
252 | reportPath = match.captured(); |
253 | } |
254 | } |
255 | return reportPath; |
256 | } |
257 | |
258 | OutputPane::AppendMode CodePorting::parseFormat(const QString &line) |
259 | { |
260 | OutputPane::AppendMode mode = OutputPane::Normal; |
261 | QRegularExpression reg("Running task:" ); |
262 | auto match = reg.match(line); |
263 | if (match.hasMatch()) { |
264 | mode = OutputPane::OverWrite; |
265 | } |
266 | return mode; |
267 | } |
268 | |