aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYasser Grimes <[email protected]>2023-07-12 17:23:44 +0300
committerYasser Grimes <[email protected]>2023-09-14 12:02:10 +0000
commit7295bc96ccf9548d0f07051979f8395fd57a3fd4 (patch)
tree19de09b027f9fd095bc15f8793f0e0d8e5783662
parent0b08a9752ab0ef396d9ac5b16638dff6569c72c5 (diff)
McuSupport: Make QmlProject c++ interfaces accecible to the CodeModel
QtMCUs relies on the QmlProject file to list c++ interfaces that are available as components in Qml, roughly similar to qmlRegisterType. Prior to this commit there was no possible methode for QtCreator to know about thos c++ interfaces so whenever there are used "Uknown Components" error is shown. After building the project qmlprojectexporter through qmlinterfacegenerator generate Qml files for each .h interface in the "CMakeFiles/<target>.dir/*.qml" or ".../<target.dir>/<module>/*.qml" To make those documents available we make use of CustomImportProvider imports: for all importable qmlproject modules loadBuiltins (new): for documents that should be available implicitly for both the signatures is changed as all created documents require the snapshot object (insert functioin) to bound their lifetime to the snapshot as they are created as shared_ptrs. duo to passing snapshot as copies between threads, storing those documents locally will cause QtC to crash Task-number: UL-6167 Change-Id: I09781f6d4b62b203944bf30ab322502d25263b56 Reviewed-by: Alessandro Portale <[email protected]>
-rw-r--r--src/libs/qmljs/qmljsinterpreter.h13
-rw-r--r--src/libs/qmljs/qmljslink.cpp9
-rw-r--r--src/plugins/mcusupport/CMakeLists.txt1
-rw-r--r--src/plugins/mcusupport/mcusupport.qbs2
-rw-r--r--src/plugins/mcusupport/mcusupportimportprovider.cpp185
-rw-r--r--src/plugins/mcusupport/mcusupportimportprovider.h48
-rw-r--r--src/plugins/mcusupport/mcusupportplugin.cpp2
7 files changed, 257 insertions, 3 deletions
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 8574e37febe..8ca9f4ec26c 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -3,10 +3,11 @@
#pragma once
+#include "qmljsdocument.h"
+#include <qmljs/parser/qmljsastfwd_p.h>
#include <qmljs/qmljs_global.h>
#include <qmljs/qmljsconstants.h>
#include <qmljs/qmljsimportdependencies.h>
-#include <qmljs/parser/qmljsastfwd_p.h>
#include <languageutils/fakemetaobject.h>
@@ -1110,12 +1111,20 @@ class QMLJS_EXPORT CustomImportsProvider : public QObject
{
Q_OBJECT
public:
+ typedef QHash<const Document *, QSharedPointer<const Imports>> ImportsPerDocument;
explicit CustomImportsProvider(QObject *parent = nullptr);
virtual ~CustomImportsProvider();
static const QList<CustomImportsProvider *> allProviders();
- virtual QList<Import> imports(ValueOwner *valueOwner, const Document *context) const = 0;
+ virtual QList<Import> imports(ValueOwner *valueOwner,
+ const Document *context,
+ Snapshot *snapshot = nullptr) const = 0;
+ virtual void loadBuiltins([[maybe_unused]] ImportsPerDocument *importsPerDocument,
+ [[maybe_unused]] Imports *imports,
+ [[maybe_unused]] const Document *context,
+ [[maybe_unused]] ValueOwner *valueOwner,
+ [[maybe_unused]] Snapshot *snapshot) {}
};
} // namespace QmlJS
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 7a34fbaa10d..b837037b7c8 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -209,9 +209,16 @@ Context::ImportsPerDocument LinkPrivate::linkImports()
// Add custom imports for the opened document
for (const auto &provider : CustomImportsProvider::allProviders()) {
- const auto providerImports = provider->imports(m_valueOwner, document.data());
+ const auto providerImports = provider->imports(m_valueOwner,
+ document.data(),
+ &m_snapshot);
for (const auto &import : providerImports)
importCache.insert(ImportCacheKey(import.info), import);
+ provider->loadBuiltins(&importsPerDocument,
+ imports,
+ document.data(),
+ m_valueOwner,
+ &m_snapshot);
}
populateImportedTypes(imports, document);
diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt
index c5ce1bfa64c..f52c31b82ab 100644
--- a/src/plugins/mcusupport/CMakeLists.txt
+++ b/src/plugins/mcusupport/CMakeLists.txt
@@ -25,6 +25,7 @@ add_qtc_plugin(McuSupport
mcuqmlprojectnode.cpp mcuqmlprojectnode.h
mcubuildstep.cpp mcubuildstep.h
dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h
+ mcusupportimportprovider.cpp mcusupportimportprovider.h
)
add_subdirectory(test)
diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs
index 752b6a3e6ff..96fea109d75 100644
--- a/src/plugins/mcusupport/mcusupport.qbs
+++ b/src/plugins/mcusupport/mcusupport.qbs
@@ -60,6 +60,8 @@ QtcPlugin {
"settingshandler.cpp",
"dialogs/mcukitcreationdialog.h",
"dialogs/mcukitcreationdialog.cpp",
+ "mcusupportimportprovider.h",
+ "mcusupportimportprovider.cpp",
]
QtcTestFiles {
diff --git a/src/plugins/mcusupport/mcusupportimportprovider.cpp b/src/plugins/mcusupport/mcusupportimportprovider.cpp
new file mode 100644
index 00000000000..b78baf8bd94
--- /dev/null
+++ b/src/plugins/mcusupport/mcusupportimportprovider.cpp
@@ -0,0 +1,185 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "mcusupportimportprovider.h"
+
+#include <cmakeprojectmanager/cmakeprojectconstants.h>
+#include <languageutils/componentversion.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectnodes.h>
+#include <qmljs/qmljsbind.h>
+#include <qmljs/qmljsdocument.h>
+#include <qmljs/qmljsinterpreter.h>
+#include <qmljs/qmljsvalueowner.h>
+
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QRegularExpression>
+
+#include <optional>
+
+static const char uriPattern[]{R"(uri\s*:\s*[\'\"](\w+)[\'\"])"};
+
+namespace McuSupport::Internal {
+using namespace ProjectExplorer;
+
+// Get the MCU target build folder for a file
+static std::optional<FilePath> getTargetBuildFolder(const FilePath &path)
+{
+ const Project *project = ProjectExplorer::ProjectManager::projectForFile(path);
+ if (!project)
+ return std::nullopt;
+ auto node = project->nodeForFilePath(path);
+ if (!node)
+ return std::nullopt;
+
+ // Get the cmake target node (CMakeTargetNode is internal)
+ // Go up in hierarchy until the first CMake target node
+ const ProjectNode *targetNode = nullptr;
+ FilePath projectBuildFolder;
+ while (node) {
+ targetNode = node->asProjectNode();
+ if (!targetNode) {
+ node = node->parentProjectNode();
+ continue;
+ }
+ projectBuildFolder = FilePath::fromVariant(
+ targetNode->data(CMakeProjectManager::Constants::BUILD_FOLDER_ROLE));
+ if (!projectBuildFolder.isDir()) {
+ node = node->parentProjectNode();
+ targetNode = nullptr;
+ continue;
+ }
+ break;
+ }
+
+ if (!targetNode)
+ return std::nullopt;
+
+ return projectBuildFolder / "CMakeFiles" / (project->displayName() + ".dir");
+};
+
+QList<Import> McuSupportImportProvider::imports(ValueOwner *valueOwner,
+ const Document *context,
+ Snapshot *snapshot) const
+{
+ QList<Import> ret;
+
+ const FilePath path = context->fileName();
+ const std::optional<FilePath> cmakeFilesPathOpt = getTargetBuildFolder(path);
+ if (!cmakeFilesPathOpt)
+ return {};
+
+ const FilePath inputFilePath = *cmakeFilesPathOpt / "config/input.json";
+
+ const QJsonDocument doc = QJsonDocument::fromJson(inputFilePath.fileContents().value_or(""));
+ if (!doc.isObject())
+ return {};
+ const QJsonObject mainProjectObj = doc.object();
+ for (const QJsonValue &moduleValue : mainProjectObj["modulesDependencies"].toArray()) {
+ if (!moduleValue.isObject())
+ continue;
+ const FilePath modulePath = FilePath::fromUserInput(
+ moduleValue.toObject()["qmlProjectFile"].toString());
+ const QString fileContent = QString::fromLatin1(modulePath.fileContents().value_or(""));
+ QRegularExpressionMatch uriMatch = QRegularExpression(uriPattern).match(fileContent);
+ if (!uriMatch.hasMatch())
+ continue;
+ QString moduleUri = uriMatch.captured(1);
+ const FilePath moduleFolder = *cmakeFilesPathOpt / moduleUri;
+
+ Import import;
+ import.valid = true;
+ import.object = new ObjectValue(valueOwner, moduleUri);
+ import.info = ImportInfo::moduleImport(moduleUri, {1, 0}, QString());
+ for (const auto &qmlFilePath : moduleFolder.dirEntries(FileFilter({"*.qml"}, QDir::Files))) {
+ Document::MutablePtr doc = Document::create(qmlFilePath, Dialect::Qml);
+ doc->setSource(QString::fromLatin1(qmlFilePath.fileContents().value_or("")));
+ doc->parseQml();
+ snapshot->insert(doc, true);
+ import.object->setMember(doc->componentName(), doc->bind()->rootObjectValue());
+ }
+ ret << import;
+ }
+ return ret;
+};
+
+void McuSupportImportProvider::loadBuiltins(ImportsPerDocument *importsPerDocument,
+ Imports *imports,
+ const Document *context,
+ ValueOwner *valueOwner,
+ Snapshot *snapshot)
+{
+ Import import;
+ import.valid = true;
+ import.object = new ObjectValue(valueOwner, "<qul>");
+ import.info = ImportInfo::moduleImport("qul", {1, 0}, QString());
+ getInterfacesImport(context->fileName(), importsPerDocument, import, valueOwner, snapshot);
+ imports->append(import);
+};
+
+void McuSupportImportProvider::getInterfacesImport(const FilePath &path,
+ ImportsPerDocument *importsPerDocument,
+ Import &import,
+ ValueOwner *valueOwner,
+ Snapshot *snapshot) const
+{
+ const std::optional<FilePath> cmakeFilesPathOpt = getTargetBuildFolder(path);
+ if (!cmakeFilesPathOpt)
+ return;
+
+ const FilePath inputFilePath = *cmakeFilesPathOpt / "config/input.json";
+
+ std::optional<FilePath> fileModule = getFileModule(path, inputFilePath);
+ FilePath lookupDir = *cmakeFilesPathOpt
+ / (fileModule && !fileModule->isEmpty()
+ ? QRegularExpression(uriPattern)
+ .match(QString::fromLatin1(
+ fileModule->fileContents().value_or("")))
+ .captured(1)
+ : "");
+
+ for (const auto &qmlFilePath : lookupDir.dirEntries(FileFilter({"*.qml"}, QDir::Files))) {
+ Document::MutablePtr doc = Document::create(qmlFilePath, Dialect::Qml);
+ doc->setSource(QString::fromLatin1(qmlFilePath.fileContents().value_or("")));
+ doc->parseQml();
+ snapshot->insert(doc, true);
+ import.object->setMember(doc->componentName(), doc->bind()->rootObjectValue());
+ importsPerDocument->insert(doc.data(), QSharedPointer<Imports>(new Imports(valueOwner)));
+ }
+}
+
+std::optional<FilePath> McuSupportImportProvider::getFileModule(const FilePath &file,
+ const FilePath &inputFile) const
+{
+ const auto doc = QJsonDocument::fromJson(inputFile.fileContents().value_or(""));
+ if (!doc.isObject())
+ return {};
+
+ const QJsonObject mainProjectObject = doc.object();
+
+ // Mapping module objects starting with mainProject
+ const QJsonArray mainProjectQmlFiles = mainProjectObject["QmlFiles"].toArray();
+ if (std::any_of(mainProjectQmlFiles.constBegin(),
+ mainProjectQmlFiles.constEnd(),
+ [&file](const QJsonValue &value) {
+ return FilePath::fromUserInput(value.toString()) == file;
+ }))
+ return std::nullopt;
+
+ for (const QJsonValue &module : mainProjectObject["modulesDependencies"].toArray()) {
+ const QJsonObject moduleObject = module.toObject();
+ const QJsonArray moduleQmlFiles = moduleObject["QmlFiles"].toArray();
+ if (std::any_of(moduleQmlFiles.constBegin(),
+ moduleQmlFiles.constEnd(),
+ [&file](const QJsonValue &value) {
+ return FilePath::fromUserInput(value.toString()) == file;
+ }))
+ return FilePath::fromUserInput(moduleObject["qmlProjectFile"].toString());
+ }
+ return std::nullopt;
+}
+}; // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/mcusupportimportprovider.h b/src/plugins/mcusupport/mcusupportimportprovider.h
new file mode 100644
index 00000000000..829fa711505
--- /dev/null
+++ b/src/plugins/mcusupport/mcusupportimportprovider.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "mcusupport_global.h"
+#include "mcusupportplugin.h"
+
+#include <utils/filepath.h>
+#include <qmljs/qmljsdocument.h>
+#include <qmljs/qmljsinterpreter.h>
+
+namespace McuSupport::Internal {
+using namespace QmlJS;
+using namespace Utils;
+class McuSupportImportProvider : public CustomImportsProvider
+{
+public:
+ McuSupportImportProvider() {}
+ ~McuSupportImportProvider() {}
+
+ // Overridden functions
+ virtual QList<Import> imports(ValueOwner *valueOwner,
+ const Document *context,
+ Snapshot *snapshot) const override;
+ virtual void loadBuiltins(ImportsPerDocument *importsPerDocument,
+ Imports *imports,
+ const Document *context,
+ ValueOwner *valueOwner,
+ Snapshot *snapshot) override;
+
+ // Add to the interfaces needed for a document
+ // path: opened qml document
+ // importsPerDocument: imports available in the document (considered imported)
+ // import: qul import containing builtin types (interfaces)
+ // valueOwner: imports members owner
+ // snapshot: where qul documenents live
+ void getInterfacesImport(const FilePath &path,
+ ImportsPerDocument *importsPerDocument,
+ Import &import,
+ ValueOwner *valueOwner,
+ Snapshot *snapshot) const;
+
+ // Get the qmlproject module which a qmlfile is part of
+ // nullopt means the file is part of the main project
+ std::optional<FilePath> getFileModule(const FilePath &file, const FilePath &inputFile) const;
+};
+}; // namespace McuSupport::Internal
diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp
index f531b93a2aa..a9f8ff0d499 100644
--- a/src/plugins/mcusupport/mcusupportplugin.cpp
+++ b/src/plugins/mcusupport/mcusupportplugin.cpp
@@ -8,6 +8,7 @@
#include "mcuqmlprojectnode.h"
#include "mcusupportconstants.h"
#include "mcusupportdevice.h"
+#include "mcusupportimportprovider.h"
#include "mcusupportoptions.h"
#include "mcusupportoptionspage.h"
#include "mcusupportrunconfiguration.h"
@@ -102,6 +103,7 @@ public:
McuSupportOptions m_options{m_settingsHandler};
McuSupportOptionsPage optionsPage{m_options, m_settingsHandler};
MCUBuildStepFactory mcuBuildStepFactory;
+ McuSupportImportProvider mcuImportProvider;
}; // class McuSupportPluginPrivate
static McuSupportPluginPrivate *dd{nullptr};