File indexing completed on 2024-05-05 04:39:26
0001 /* 0002 SPDX-FileCopyrightText: 2017 Aleix Pol Gonzalez <aleixpol@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "cmakeserverimportjob.h" 0008 #include "cmakeutils.h" 0009 #include "cmakeserver.h" 0010 0011 #include <interfaces/iproject.h> 0012 #include <interfaces/icore.h> 0013 #include <interfaces/iruntime.h> 0014 #include <interfaces/iruntimecontroller.h> 0015 #include <makefileresolver/makefileresolver.h> 0016 0017 #include <QJsonObject> 0018 #include <QJsonArray> 0019 #include <QRegularExpression> 0020 #include <QFileInfo> 0021 0022 #include "debug.h" 0023 0024 void CMakeServerImportJob::processCodeModel(const QJsonObject &response, CMakeProjectData &data) 0025 { 0026 const auto configs = response.value(QStringLiteral("configurations")).toArray(); 0027 qCDebug(CMAKE) << "process response" << response; 0028 0029 data.targets.clear(); 0030 data.compilationData.files.clear(); 0031 0032 StringInterner stringInterner; 0033 0034 const auto rt = KDevelop::ICore::self()->runtimeController()->currentRuntime(); 0035 for (const auto &config: configs) { 0036 const auto projects = config.toObject().value(QStringLiteral("projects")).toArray(); 0037 for (const auto &project: projects) { 0038 const auto targets = project.toObject().value(QStringLiteral("targets")).toArray(); 0039 for (const auto &targetObject: targets) { 0040 const auto target = targetObject.toObject(); 0041 const KDevelop::Path targetDir = rt->pathInHost(KDevelop::Path(target.value(QStringLiteral("sourceDirectory")).toString())); 0042 0043 KDevelop::Path::List targetSources; 0044 const auto fileGroups = target.value(QStringLiteral("fileGroups")).toArray(); 0045 for (const auto &fileGroupValue: fileGroups) { 0046 const auto fileGroup = fileGroupValue.toObject(); 0047 CMakeFile file; 0048 file.includes = kTransform<KDevelop::Path::List>(fileGroup.value(QStringLiteral("includePath")).toArray(), [](const QJsonValue& val) { return KDevelop::Path(val.toObject().value(QStringLiteral("path")).toString()); }); 0049 0050 file.language = fileGroup.value(QStringLiteral("language")).toString(), 0051 file.compileFlags = fileGroup.value(QStringLiteral("compileFlags")).toString(); 0052 for (const auto& jsonDefine : fileGroup.value(QStringLiteral("defines")).toArray()) { 0053 file.addDefine(jsonDefine.toString()); 0054 } 0055 file.defines = MakeFileResolver::extractDefinesFromCompileFlags(file.compileFlags, stringInterner, file.defines); 0056 0057 // apparently some file groups do not contain build system information 0058 // skip these, as they would produce bogus results for us and break the fallback 0059 // implemented in CMakeManager::fileInformation 0060 if (file.isEmpty()) { 0061 continue; 0062 } 0063 0064 const auto sourcesArray = fileGroup.value(QStringLiteral("sources")).toArray(); 0065 const KDevelop::Path::List sources = kTransform<KDevelop::Path::List>(sourcesArray, [targetDir](const QJsonValue& val) { return KDevelop::Path(targetDir, val.toString()); }); 0066 targetSources.reserve(targetSources.size() + sources.size()); 0067 for (const auto& source: sources) { 0068 // NOTE: we use the canonical file path to prevent issues with symlinks in the path 0069 // leading to lookup failures 0070 const auto localFile = rt->pathInHost(source); 0071 const auto canonicalFile = QFileInfo(source.toLocalFile()).canonicalFilePath(); 0072 const auto sourcePath = (canonicalFile.isEmpty() || localFile.toLocalFile() == canonicalFile) 0073 ? localFile : KDevelop::Path(canonicalFile); 0074 data.compilationData.files[sourcePath] = file; 0075 targetSources << sourcePath; 0076 } 0077 qCDebug(CMAKE) << "registering..." << sources << file; 0078 } 0079 0080 CMakeTarget cmakeTarget{ 0081 CMakeTarget::typeToEnum(target.value(QLatin1String("type")).toString()), 0082 target.value(QStringLiteral("name")).toString(), 0083 kTransform<KDevelop::Path::List>(target[QLatin1String("artifacts")].toArray(), [](const QJsonValue& val) { return KDevelop::Path(val.toString()); }), 0084 targetSources, 0085 QString() 0086 }; 0087 0088 // ensure we don't add the same target multiple times, for different projects 0089 // cf.: https://bugs.kde.org/show_bug.cgi?id=387095 0090 auto& dirTargets = data.targets[targetDir]; 0091 if (dirTargets.contains(cmakeTarget)) 0092 continue; 0093 dirTargets += cmakeTarget; 0094 0095 qCDebug(CMAKE) << "adding target" << cmakeTarget.name << "with sources" << cmakeTarget.sources; 0096 } 0097 } 0098 } 0099 } 0100 0101 CMakeServerImportJob::CMakeServerImportJob(KDevelop::IProject* project, const QSharedPointer<CMakeServer> &server, QObject* parent) 0102 : KJob(parent) 0103 , m_server(server) 0104 , m_project(project) 0105 { 0106 connect(m_server.data(), &CMakeServer::disconnected, this, [this]() { 0107 setError(UnexpectedDisconnect); 0108 emitResult(); 0109 }); 0110 } 0111 0112 void CMakeServerImportJob::start() 0113 { 0114 if (m_server->isServerAvailable()) 0115 doStart(); 0116 else 0117 connect(m_server.data(), &CMakeServer::connected, this, &CMakeServerImportJob::doStart); 0118 } 0119 0120 void CMakeServerImportJob::doStart() 0121 { 0122 connect(m_server.data(), &CMakeServer::response, this, &CMakeServerImportJob::processResponse); 0123 0124 m_server->handshake(m_project->path(), CMake::currentBuildDir(m_project)); 0125 } 0126 0127 void CMakeServerImportJob::processResponse(const QJsonObject& response) 0128 { 0129 const auto responseType = response.value(QStringLiteral("type")); 0130 if (responseType == QLatin1String("reply")) { 0131 const auto inReplyTo = response.value(QStringLiteral("inReplyTo")); 0132 qCDebug(CMAKE) << "replying..." << inReplyTo; 0133 if (inReplyTo == QLatin1String("handshake")) { 0134 m_server->configure({}); 0135 } else if (inReplyTo == QLatin1String("configure")) { 0136 m_server->compute(); 0137 } else if (inReplyTo == QLatin1String("compute")) { 0138 m_server->codemodel(); 0139 } else if(inReplyTo == QLatin1String("codemodel")) { 0140 processCodeModel(response, m_data); 0141 m_data.testSuites = CMake::importTestSuites(CMake::currentBuildDir(m_project)); 0142 m_data.compilationData.rebuildFileForFolderMapping(); 0143 emitResult(); 0144 } else { 0145 qCDebug(CMAKE) << "unhandled reply" << response; 0146 } 0147 } else if(responseType == QLatin1String("error")) { 0148 setError(ErrorResponse); 0149 setErrorText(response.value(QStringLiteral("errorMessage")).toString()); 0150 qCWarning(CMAKE) << "error!!" << response; 0151 emitResult(); 0152 } else if (responseType == QLatin1String("progress")) { 0153 int progress = response.value(QStringLiteral("progressCurrent")).toInt(); 0154 int total = response.value(QStringLiteral("progressMaximum")).toInt(); 0155 if (progress >= 0 && total > 0) { 0156 setPercent(100.0 * progress / total); 0157 } 0158 } else if (responseType == QLatin1String("message") || responseType == QLatin1String("hello")) { 0159 // Known, but not used for anything currently. 0160 } else { 0161 qCDebug(CMAKE) << "unhandled message" << response; 0162 } 0163 } 0164 0165 #include "moc_cmakeserverimportjob.cpp"