1 /*
2 SPDX-FileCopyrightText: 2018, 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "compileanalyzeutils.h"
8
9 // lib
10 #include <debug.h>
11 // KDevPlatform
12 #include <util/path.h>
13 // KF
14 #include <KLocalizedString>
15 // Qt
16 #include <QStandardPaths>
17 #include <QUrl>
18 #include <QFile>
19 #include <QFileInfo>
20 #include <QJsonDocument>
21 #include <QJsonArray>
22 #include <QJsonObject>
23
24 namespace KDevelop
25 {
26
27 namespace Utils
28 {
29
findExecutable(const QString & fallbackExecutablePath)30 QString findExecutable(const QString& fallbackExecutablePath)
31 {
32 const QString executablePath = QStandardPaths::findExecutable(fallbackExecutablePath);
33 return executablePath.isEmpty() ? fallbackExecutablePath : executablePath;
34 }
35
filesFromCompilationDatabase(const KDevelop::Path & buildPath,const QUrl & urlToCheck,bool allFiles,QString & error)36 QStringList filesFromCompilationDatabase(const KDevelop::Path& buildPath,
37 const QUrl& urlToCheck, bool allFiles,
38 QString& error)
39 {
40 QStringList result;
41
42 const auto commandsFilePath = KDevelop::Path(buildPath, QStringLiteral("compile_commands.json")).toLocalFile();
43
44 if (!QFile::exists(commandsFilePath)) {
45 error = i18n("Compilation database file not found: '%1'", commandsFilePath);
46 return result;
47 }
48
49 const auto pathToCheck = urlToCheck.toLocalFile();
50 if (pathToCheck.isEmpty()) {
51 error = i18n("Nothing to check: compilation database file '%1' contains no matching items.", commandsFilePath);
52 return result;
53 }
54
55 QFile commandsFile(commandsFilePath);
56 if (!commandsFile.open(QFile::ReadOnly | QFile::Text)) {
57 error = i18n("Could not open compilation database file for reading: '%1'", commandsFilePath);
58 return result;
59 }
60
61 QJsonParseError jsonError;
62 const auto commandsDocument = QJsonDocument::fromJson(commandsFile.readAll(), &jsonError);
63
64 if (jsonError.error) {
65 error = i18n("JSON error during parsing compilation database file '%1': %2", commandsFilePath, jsonError.errorString());
66 return result;
67 }
68
69 if (!commandsDocument.isArray()) {
70 error = i18n("JSON error during parsing compilation database file '%1': document is not an array.", commandsFilePath);
71 return result;
72 }
73
74 const auto pathToCheckInfo = QFileInfo(pathToCheck);
75 const bool isPathToCheckAFile = pathToCheckInfo.isFile();
76 const auto canonicalPathToCheck = pathToCheckInfo.canonicalFilePath();
77
78 const auto fileDataArray = commandsDocument.array();
79 for (const auto& value : fileDataArray) {
80 if (!value.isObject()) {
81 continue;
82 }
83
84 const auto entry = value.toObject();
85 const auto it = entry.find(QLatin1String("file"));
86 if (it != entry.end()) {
87 // using the original path from the commands file
88 // but matching the canonical ones
89 const auto path = it->toString();
90 const auto pathInfo = QFileInfo(path);
91 if (pathInfo.exists()) {
92 if (allFiles) {
93 result += path;
94 } else {
95 const auto canonicalPath = pathInfo.canonicalFilePath();
96 if (isPathToCheckAFile) {
97 if (canonicalPath == canonicalPathToCheck) {
98 result = QStringList{path};
99 break;
100 }
101 } else if (canonicalPath.startsWith(canonicalPathToCheck)) {
102 result.append(path);
103 }
104 }
105 }
106 }
107 }
108
109 if (result.isEmpty()) {
110 error = i18n("Nothing to check: compilation database file '%1' contains no matching items.", commandsFilePath);
111 }
112
113 return result;
114 }
115
116 }
117
118 }
119