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