File indexing completed on 2024-05-05 04:39:28

0001 /*
0002     SPDX-FileCopyrightText: 2018, 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "compileanalyzeutils.h"
0008 
0009 // lib
0010 #include <debug.h>
0011 // KDevPlatform
0012 #include <util/path.h>
0013 // KF
0014 #include <KLocalizedString>
0015 // Qt
0016 #include <QStandardPaths>
0017 #include <QUrl>
0018 #include <QFile>
0019 #include <QFileInfo>
0020 #include <QJsonDocument>
0021 #include <QJsonArray>
0022 #include <QJsonObject>
0023 
0024 namespace KDevelop
0025 {
0026 
0027 namespace Utils
0028 {
0029 
0030 QString findExecutable(const QString& fallbackExecutablePath)
0031 {
0032     const QString executablePath = QStandardPaths::findExecutable(fallbackExecutablePath);
0033     return executablePath.isEmpty() ? fallbackExecutablePath : executablePath;
0034 }
0035 
0036 QStringList filesFromCompilationDatabase(const KDevelop::Path& buildPath,
0037                                          const QUrl& urlToCheck, bool allFiles,
0038                                          QString& error)
0039 {
0040     QStringList result;
0041 
0042     const auto commandsFilePath = KDevelop::Path(buildPath, QStringLiteral("compile_commands.json")).toLocalFile();
0043 
0044     if (!QFile::exists(commandsFilePath)) {
0045         error = i18n("Compilation database file not found: '%1'", commandsFilePath);
0046         return result;
0047     }
0048 
0049     const auto pathToCheck = urlToCheck.toLocalFile();
0050     if (pathToCheck.isEmpty()) {
0051         error = i18n("Nothing to check: compilation database file '%1' contains no matching items.", commandsFilePath);
0052         return result;
0053     }
0054 
0055     QFile commandsFile(commandsFilePath);
0056     if (!commandsFile.open(QFile::ReadOnly | QFile::Text)) {
0057         error = i18n("Could not open compilation database file for reading: '%1'", commandsFilePath);
0058         return result;
0059     }
0060 
0061     QJsonParseError jsonError;
0062     const auto commandsDocument = QJsonDocument::fromJson(commandsFile.readAll(), &jsonError);
0063 
0064     if (jsonError.error) {
0065         error = i18n("JSON error during parsing compilation database file '%1': %2", commandsFilePath, jsonError.errorString());
0066         return result;
0067     }
0068 
0069     if (!commandsDocument.isArray()) {
0070         error = i18n("JSON error during parsing compilation database file '%1': document is not an array.", commandsFilePath);
0071         return result;
0072     }
0073 
0074     const auto pathToCheckInfo = QFileInfo(pathToCheck);
0075     const bool isPathToCheckAFile = pathToCheckInfo.isFile();
0076     const auto canonicalPathToCheck = pathToCheckInfo.canonicalFilePath();
0077 
0078     const auto fileDataArray = commandsDocument.array();
0079     for (const auto& value : fileDataArray) {
0080         if (!value.isObject()) {
0081             continue;
0082         }
0083 
0084         const auto entry = value.toObject();
0085         const auto it = entry.find(QLatin1String("file"));
0086         if (it != entry.end()) {
0087             // using the original path from the commands file
0088             // but matching the canonical ones
0089             const auto path = it->toString();
0090             const auto pathInfo = QFileInfo(path);
0091             if (pathInfo.exists()) {
0092                 if (allFiles) {
0093                     result += path;
0094                 } else {
0095                     const auto canonicalPath = pathInfo.canonicalFilePath();
0096                     if (isPathToCheckAFile) {
0097                         if (canonicalPath == canonicalPathToCheck) {
0098                             result = QStringList{path};
0099                             break;
0100                         }
0101                     } else if (canonicalPath.startsWith(canonicalPathToCheck)) {
0102                         result.append(path);
0103                     }
0104                 }
0105             }
0106         }
0107     }
0108 
0109     if (result.isEmpty()) {
0110         error = i18n("Nothing to check: compilation database file '%1' contains no matching items.", commandsFilePath);
0111     }
0112 
0113     return result;
0114 }
0115 
0116 }
0117 
0118 }