| 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 |  | 
|---|