aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmlprojectmanager
diff options
context:
space:
mode:
authorTim Jenssen <[email protected]>2023-05-22 19:48:42 +0200
committerTim Jenssen <[email protected]>2023-05-22 21:13:40 +0200
commit1b6c0ff56c8d48c5b5bb8044b113cd580f6d5ee3 (patch)
tree25878f9091506fd282a338a2c5f98b357d47134e /src/plugins/qmlprojectmanager
parent3dcdbe9069c452e2f0eacb925aa7412e63dc4762 (diff)
parentdf7398e2c5f3c1595f32c7484ac1e804d83a01ca (diff)
Merge remote-tracking branch 'origin/qds/dev'
Conflicts: coin/instructions/build.yaml doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl src/libs/CMakeLists.txt src/libs/advanceddockingsystem/dockmanager.cpp src/libs/advanceddockingsystem/workspacedialog.cpp src/plugins/coreplugin/manhattanstyle.cpp src/plugins/qmldesigner/CMakeLists.txt src/plugins/qmldesigner/components/formeditor/toolbox.cpp src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp src/plugins/qmldesignerbase/qmldesignerbaseplugin.h src/plugins/qmlprojectmanager/qmlproject.cpp src/tools/qml2puppet/CMakeLists.txt tests/unit/unittest/CMakeLists.txt Change-Id: I2c5f18c4fca49471d02713ce5859032232cf7756
Diffstat (limited to 'src/plugins/qmlprojectmanager')
-rw-r--r--src/plugins/qmlprojectmanager/CMakeLists.txt60
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp368
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h19
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp (renamed from src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp)75
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h (renamed from src/plugins/qmlprojectmanager/fileformat/filefilteritems.h)33
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp402
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h116
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.cpp (renamed from src/plugins/qmlprojectmanager/qmlprojectnodes.cpp)4
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.h (renamed from src/plugins/qmlprojectmanager/qmlprojectnodes.h)0
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp598
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h120
-rw-r--r--src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp198
-rw-r--r--src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h22
-rw-r--r--src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp118
-rw-r--r--src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h103
-rw-r--r--src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.cpp784
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.h162
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectmanager.qbs14
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.cpp1
20 files changed, 1861 insertions, 1338 deletions
diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt
index 4119bae7347..ad71e3fbffb 100644
--- a/src/plugins/qmlprojectmanager/CMakeLists.txt
+++ b/src/plugins/qmlprojectmanager/CMakeLists.txt
@@ -1,21 +1,9 @@
add_qtc_plugin(QmlProjectManager
CONDITION TARGET Qt::QuickWidgets
- PROPERTIES COMPILE_WARNING_AS_ERROR ON
PLUGIN_CLASS QmlProjectPlugin
- DEPENDS QmlJS Qt::QuickWidgets
+ DEPENDS QmlJS Qt::QuickWidgets Utils
PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase
SOURCES
- fileformat/filefilteritems.cpp fileformat/filefilteritems.h
- fileformat/qmlprojectfileformat.cpp fileformat/qmlprojectfileformat.h
- fileformat/qmlprojectitem.cpp fileformat/qmlprojectitem.h
- cmakegen/checkablefiletreeitem.cpp cmakegen/checkablefiletreeitem.h
- cmakegen/cmakegeneratordialog.cpp cmakegen/cmakegeneratordialog.h
- cmakegen/cmakegeneratordialogtreemodel.cpp cmakegen/cmakegeneratordialogtreemodel.h
- cmakegen/cmakeprojectconverter.cpp cmakegen/cmakeprojectconverter.h
- cmakegen/cmakeprojectconverterdialog.cpp cmakegen/cmakeprojectconverterdialog.h
- cmakegen/generatecmakelists.cpp cmakegen/generatecmakelists.h
- cmakegen/generatecmakelistsconstants.h
- cmakegen/boilerplate.qrc
qmlprojectgen/qmlprojectgenerator.cpp qmlprojectgen/qmlprojectgenerator.h
qmlprojectgen/templates.qrc
projectfilecontenttools.cpp projectfilecontenttools.h
@@ -29,8 +17,52 @@ add_qtc_plugin(QmlProjectManager
qmlprojectmanager_global.h
qmlprojectmanagertr.h
qmlprojectmanagerconstants.h
- qmlprojectnodes.cpp qmlprojectnodes.h
qmlprojectplugin.cpp qmlprojectplugin.h
qmlprojectrunconfiguration.cpp qmlprojectrunconfiguration.h
+ buildsystem/qmlbuildsystem.cpp buildsystem/qmlbuildsystem.h
+
"${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc"
)
+
+extend_qtc_plugin(QmlProjectManager
+ CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR
+ PROPERTIES COMPILE_WARNING_AS_ERROR ON
+)
+
+extend_qtc_plugin(QmlProjectManager
+ PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/buildsystem
+ SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem
+ SOURCES
+ projectitem/filefilteritems.cpp projectitem/filefilteritems.h
+ projectitem/qmlprojectitem.cpp projectitem/qmlprojectitem.h
+ projectitem/converters.h projectitem/converters.cpp
+ projectnode/qmlprojectnodes.cpp projectnode/qmlprojectnodes.h
+)
+
+extend_qtc_plugin(QmlProjectManager
+ PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/cmakegen
+ SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/cmakegen
+ SOURCES
+ checkablefiletreeitem.cpp checkablefiletreeitem.h
+ cmakegeneratordialog.cpp cmakegeneratordialog.h
+ cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h
+ cmakeprojectconverter.cpp cmakeprojectconverter.h
+ cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h
+ generatecmakelists.cpp generatecmakelists.h
+ generatecmakelistsconstants.h
+ boilerplate.qrc
+)
+
+add_qtc_library(QmlProjectManagerLib OBJECT
+ CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3
+ EXCLUDE_FROM_INSTALL
+ DEPENDS
+ QmlJS Utils
+ INCLUDES
+ ${CMAKE_CURRENT_LIST_DIR}
+ SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem
+ SOURCES
+ projectitem/filefilteritems.cpp projectitem/filefilteritems.h
+ projectitem/qmlprojectitem.cpp projectitem/qmlprojectitem.h
+ projectitem/converters.cpp projectitem/converters.h
+)
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
new file mode 100644
index 00000000000..0097611a4dd
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp
@@ -0,0 +1,368 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "converters.h"
+
+#include <QJsonArray>
+
+namespace QmlProjectManager::Converters {
+
+using PropsPair = QPair<QString, QStringList>;
+struct FileProps
+{
+ const PropsPair image{"image", QStringList{"*.jpeg", "*.jpg", "*.png", "*.svg", "*.hdr", ".ktx"}};
+ const PropsPair qml{"qml", QStringList{"*.qml"}};
+ const PropsPair qmlDir{"qmldir", QStringList{"qmldir"}};
+ const PropsPair javaScr{"javaScript", QStringList{"*.js", "*.ts"}};
+ const PropsPair video{"video", QStringList{"*.mp4"}};
+ const PropsPair sound{"sound", QStringList{"*.mp3", "*.wav"}};
+ const PropsPair font{"font", QStringList{"*.ttf", "*.otf"}};
+ const PropsPair config{"config", QStringList{"*.conf"}};
+ const PropsPair styling{"styling", QStringList{"*.css"}};
+ const PropsPair mesh{"meshes", QStringList{"*.mesh"}};
+ const PropsPair
+ shader{"shader",
+ QStringList{"*.glsl", "*.glslv", "*.glslf", "*.vsh", "*.fsh", "*.vert", "*.frag"}};
+};
+
+QString jsonToQmlProject(const QJsonObject &rootObject)
+{
+ QString qmlProjectString;
+ QTextStream ts{&qmlProjectString};
+
+ QJsonObject runConfig = rootObject["runConfig"].toObject();
+ QJsonObject languageConfig = rootObject["language"].toObject();
+ QJsonObject shaderConfig = rootObject["shaderTool"].toObject();
+ QJsonObject versionConfig = rootObject["versions"].toObject();
+ QJsonObject environmentConfig = rootObject["environment"].toObject();
+ QJsonObject deploymentConfig = rootObject["deployment"].toObject();
+ QJsonObject filesConfig = rootObject["fileGroups"].toObject();
+
+ int indentationLevel = 0;
+
+ auto appendBreak = [&ts]() { ts << Qt::endl; };
+
+ auto appendComment = [&ts, &indentationLevel](const QString &comment) {
+ ts << QString(" ").repeated(indentationLevel * 4) << "\\\\ " << comment << Qt::endl;
+ };
+
+ auto appendItem =
+ [&ts, &indentationLevel](const QString &key, const QString &value, const bool isEnclosed) {
+ ts << QString(" ").repeated(indentationLevel * 4) << key << ": "
+ << (isEnclosed ? "\"" : "") << value << (isEnclosed ? "\"" : "") << Qt::endl;
+ };
+
+ auto appendString = [&appendItem](const QString &key, const QString &val) {
+ appendItem(key, val, true);
+ };
+
+ auto appendBool = [&appendItem](const QString &key, const bool &val) {
+ appendItem(key, QString::fromStdString(val ? "true" : "false"), false);
+ };
+
+ auto appendArray = [&appendItem](const QString &key, const QStringList &vals) {
+ QString finalString;
+ foreach (const QString &value, vals) {
+ finalString.append("\"").append(value).append("\"").append(",");
+ }
+ finalString.remove(finalString.length() - 1, 1);
+ finalString.prepend("[ ").append(" ]");
+ appendItem(key, finalString, false);
+ };
+
+ auto startObject = [&ts, &indentationLevel](const QString &objectName) {
+ ts << Qt::endl
+ << QString(" ").repeated(indentationLevel * 4) << objectName << " {" << Qt::endl;
+ indentationLevel++;
+ };
+
+ auto endObject = [&ts, &indentationLevel]() {
+ indentationLevel--;
+ ts << QString(" ").repeated(indentationLevel * 4) << "}" << Qt::endl;
+ };
+
+ auto appendDirectories =
+ [&startObject, &endObject, &appendString, &filesConfig](const QString &jsonKey,
+ const QString &qmlKey) {
+ QJsonValue dirsObj = filesConfig[jsonKey].toObject()["directories"];
+ QStringList dirs = dirsObj.toVariant().toStringList();
+ foreach (const QString &directory, dirs) {
+ startObject(qmlKey);
+ appendString("directory", directory);
+ endObject();
+ }
+ };
+
+ auto appendFiles = [&startObject,
+ &endObject,
+ &appendString,
+ &appendArray,
+ &filesConfig](const QString &jsonKey, const QString &qmlKey) {
+ QJsonValue dirsObj = filesConfig[jsonKey].toObject()["directories"];
+ QJsonValue filesObj = filesConfig[jsonKey].toObject()["files"];
+ QJsonValue filtersObj = filesConfig[jsonKey].toObject()["filters"];
+
+ foreach (const QString &directory, dirsObj.toVariant().toStringList()) {
+ startObject(qmlKey);
+ appendString("directory", directory);
+ appendString("filters", filtersObj.toVariant().toStringList().join(";"));
+
+ if (!filesObj.toArray().isEmpty()) {
+ QStringList fileList;
+ foreach (const QJsonValue &file, filesObj.toArray()) {
+ fileList.append(file.toObject()["name"].toString());
+ }
+ appendArray("files", fileList);
+ }
+ endObject();
+ }
+ };
+
+ // start creating the file content
+ appendComment("prop: json-converted");
+ appendComment("prop: auto-generated");
+
+ ts << Qt::endl << "import QmlProject" << Qt::endl;
+ {
+ startObject("Project");
+
+ { // append non-object props
+ appendString("mainFile", runConfig["mainFile"].toString());
+ appendString("mainUiFile", runConfig["mainUiFile"].toString());
+ appendString("targetDirectory", deploymentConfig["targetDirectory"].toString());
+ appendBool("widgetApp", runConfig["widgetApp"].toBool());
+ appendArray("importPaths", rootObject["importPaths"].toVariant().toStringList());
+ appendBreak();
+ appendString("qdsVersion", versionConfig["designStudio"].toString());
+ appendString("quickVersion", versionConfig["qtQuick"].toString());
+ appendBool("qt6Project", versionConfig["qt"].toString() == "6");
+ appendBool("qtForMCUs", rootObject["mcuConfig"].toObject().isEmpty());
+ appendBreak();
+ appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool());
+ appendString("primaryLanguage", languageConfig["primaryLanguage"].toString());
+ appendArray("supportedLanguages",
+ languageConfig["supportedLanguages"].toVariant().toStringList());
+ }
+
+ { // append Environment object
+ startObject("Environment");
+ foreach (const QString &key, environmentConfig.keys()) {
+ appendItem(key, environmentConfig[key].toString(), true);
+ }
+ endObject();
+ }
+
+ { // append ShaderTool object
+ startObject("ShaderTool");
+ appendString("args", shaderConfig["args"].toVariant().toStringList().join(" "));
+ appendArray("files", shaderConfig["files"].toVariant().toStringList());
+ endObject();
+ }
+
+ { // append files objects
+ appendDirectories("qml", "QmlFiles");
+ appendDirectories("javaScript", "JavaScriptFiles");
+ appendDirectories("image", "ImageFiles");
+ appendFiles("config", "Files");
+ appendFiles("font", "Files");
+ appendFiles("meshes", "Files");
+ appendFiles("qmldir", "Files");
+ appendFiles("shader", "Files");
+ appendFiles("sound", "Files");
+ appendFiles("video", "Files");
+ }
+
+ endObject(); // Closing 'Project'
+ }
+ return qmlProjectString;
+}
+
+QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile)
+{
+ QmlJS::SimpleReader simpleQmlJSReader;
+
+ const QmlJS::SimpleReaderNode::Ptr rootNode = simpleQmlJSReader.readFile(projectFile.toString());
+
+ if (!simpleQmlJSReader.errors().isEmpty() || !rootNode->isValid()) {
+ qCritical() << "Unable to parse:" << projectFile;
+ qCritical() << simpleQmlJSReader.errors();
+ return {};
+ }
+
+ if (rootNode->name() != QLatin1String("Project")) {
+ qCritical() << "Cannot find root 'Proejct' item in the project file: " << projectFile;
+ return {};
+ }
+
+ auto nodeToJsonObject = [](const QmlJS::SimpleReaderNode::Ptr &node) {
+ QJsonObject tObj;
+ foreach (const QString &childPropName, node->propertyNames()) {
+ tObj.insert(childPropName, node->property(childPropName).value.toJsonValue());
+ }
+ return tObj;
+ };
+
+ auto toCamelCase = [](const QString &s) { return QString(s).replace(0, 1, s[0].toLower()); };
+
+ QJsonObject rootObject; // root object
+ QJsonObject fileGroupsObject;
+ QJsonObject languageObject;
+ QJsonObject versionObject;
+ QJsonObject runConfigObject;
+ QJsonObject deploymentObject;
+ QJsonObject mcuObject;
+ QJsonObject shaderToolObject;
+
+ // convert the the non-object props
+ for (const QString &propName : rootNode->propertyNames()) {
+ QJsonObject *currentObj = &rootObject;
+ QString objKey = propName;
+ QJsonValue value = rootNode->property(propName).value.toJsonValue();
+
+ if (propName.contains("language", Qt::CaseInsensitive)) {
+ currentObj = &languageObject;
+ if (propName.toLower() == "multilanguagesupport") // fixing the camelcase
+ objKey = "multiLanguageSupport";
+ } else if (propName.contains("version", Qt::CaseInsensitive)) {
+ currentObj = &versionObject;
+ if (propName.toLower() == "qdsversion")
+ objKey = "designStudio";
+ else if (propName.toLower() == "quickversion")
+ objKey = "qtQuick";
+ } else if (propName.contains("widgetapp", Qt::CaseInsensitive)
+ || propName.contains("fileselector", Qt::CaseInsensitive)
+ || propName.contains("mainfile", Qt::CaseInsensitive)
+ || propName.contains("mainuifile", Qt::CaseInsensitive)
+ || propName.contains("forcefreetype", Qt::CaseInsensitive)) {
+ currentObj = &runConfigObject;
+ } else if (propName.contains("targetdirectory", Qt::CaseInsensitive)) {
+ currentObj = &deploymentObject;
+ } else if (propName.contains("qtformcus", Qt::CaseInsensitive)) {
+ currentObj = &mcuObject;
+ objKey = "mcuEnabled";
+ } else if (propName.contains("qt6project", Qt::CaseInsensitive)) {
+ currentObj = &versionObject;
+ objKey = "qt";
+ value = rootNode->property(propName).value.toBool() ? "6" : "5";
+ }
+
+ currentObj->insert(objKey, value);
+ }
+
+ // add missing non-object props if any
+ if (!runConfigObject.contains("fileSelectors")) {
+ runConfigObject.insert("fileSelectors", QJsonArray{});
+ }
+
+ if (!versionObject.contains("qt")) {
+ versionObject.insert("qt", "5");
+ }
+
+ // convert the the object props
+ for (const QmlJS::SimpleReaderNode::Ptr &childNode : rootNode->children()) {
+ if (childNode->name().contains("files", Qt::CaseInsensitive)) {
+ PropsPair propsPair;
+ FileProps fileProps;
+ const QString childNodeName = childNode->name().toLower();
+ const QmlJS::SimpleReaderNode::Property childNodeFilter = childNode->property("filter");
+ const QmlJS::SimpleReaderNode::Property childNodeDirectory = childNode->property("directory");
+ const QmlJS::SimpleReaderNode::Property childNodeFiles = childNode->property("files");
+ const QString childNodeFilterValue = childNodeFilter.value.toString();
+
+ if (childNodeName == "qmlfiles" || childNodeFilterValue.contains("*.qml")) {
+ propsPair = fileProps.qml;
+ } else if (childNodeName == "javascriptfiles") {
+ propsPair = fileProps.javaScr;
+ } else if (childNodeName == "imagefiles") {
+ propsPair = fileProps.image;
+ } else {
+ if (childNodeFilter.isValid()) {
+ if (childNodeFilterValue.contains(".conf"))
+ propsPair = fileProps.config;
+ else if (childNodeFilterValue.contains(".ttf"))
+ propsPair = fileProps.font;
+ else if (childNodeFilterValue.contains("qmldir"))
+ propsPair = fileProps.qmlDir;
+ else if (childNodeFilterValue.contains(".wav"))
+ propsPair = fileProps.sound;
+ else if (childNodeFilterValue.contains(".mp4"))
+ propsPair = fileProps.video;
+ else if (childNodeFilterValue.contains(".mesh"))
+ propsPair = fileProps.mesh;
+ else if (childNodeFilterValue.contains(".glsl"))
+ propsPair = fileProps.shader;
+ else if (childNodeFilterValue.contains(".css"))
+ propsPair = fileProps.styling;
+ }
+ }
+
+ // get all objects we'll work on
+ QJsonObject targetObject = fileGroupsObject[propsPair.first].toObject();
+ QJsonArray directories = targetObject["directories"].toArray();
+ QJsonArray filters = targetObject["filters"].toArray();
+ QJsonArray files = targetObject["files"].toArray();
+
+ // populate & update filters
+ if (filters.isEmpty()) {
+ filters = QJsonArray::fromStringList(propsPair.second); // populate the filters with the predefined ones
+ }
+
+ if (childNodeFilter.isValid()) { // append filters from qmlproject (merge)
+ const QStringList filtersFromProjectFile = childNodeFilterValue.split(
+ ";");
+ for (const QString &filter : filtersFromProjectFile) {
+ if (!filters.contains(QJsonValue(filter))) {
+ filters.append(QJsonValue(filter));
+ }
+ }
+ }
+
+ // populate & update directories
+ if (childNodeDirectory.isValid()) {
+ directories.append(childNodeDirectory.value.toJsonValue());
+ }
+ if (directories.isEmpty())
+ directories.append(".");
+
+ // populate & update files
+ if (childNodeFiles.isValid()) {
+ foreach (const QJsonValue &file, childNodeFiles.value.toJsonArray()) {
+ files.append(QJsonObject{{"name", file.toString()}});
+ }
+ }
+
+ // put everything back into the root object
+ targetObject.insert("directories", directories);
+ targetObject.insert("filters", filters);
+ targetObject.insert("files", files);
+ fileGroupsObject.insert(propsPair.first, targetObject);
+ } else if (childNode->name().contains("shadertool", Qt::CaseInsensitive)) {
+ QStringList quotedArgs = childNode->property("args").value.toString().split('\"', Qt::SkipEmptyParts);
+ QStringList args;
+ for (int i = 0; i < quotedArgs.size(); ++i) {
+ // Each odd arg in this list is a single quoted argument, which we should
+ // not be split further
+ if (i % 2 == 0)
+ args.append(quotedArgs[i].trimmed().split(' '));
+ else
+ args.append(quotedArgs[i].prepend("\"").append("\""));
+ }
+
+ shaderToolObject.insert("args", QJsonArray::fromStringList(args));
+ shaderToolObject.insert("files", childNode->property("files").value.toJsonValue());
+ } else {
+ rootObject.insert(toCamelCase(childNode->name()), nodeToJsonObject(childNode));
+ }
+ }
+
+ rootObject.insert("fileGroups", fileGroupsObject);
+ rootObject.insert("language", languageObject);
+ rootObject.insert("versions", versionObject);
+ rootObject.insert("runConfig", runConfigObject);
+ rootObject.insert("deployment", deploymentObject);
+ rootObject.insert("mcuConfig", mcuObject);
+ rootObject.insert("shaderTool", shaderToolObject);
+ rootObject.insert("fileVersion", 1);
+ return rootObject;
+}
+} // namespace QmlProjectManager::Converters
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h
new file mode 100644
index 00000000000..b0bb98895e8
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QJsonObject>
+#include <QString>
+
+#include <utils/environment.h>
+#include <qmljs/qmljssimplereader.h>
+
+#include <QJsonArray>
+
+namespace QmlProjectManager::Converters {
+
+QString jsonToQmlProject(const QJsonObject &rootObject);
+QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile);
+
+} // namespace QmlProjectManager::Converters
diff --git a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp
index 79b490887e5..9e834891949 100644
--- a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp
@@ -15,36 +15,46 @@
namespace QmlProjectManager {
-FileFilterBaseItem::FileFilterBaseItem()
+FileFilterItem::FileFilterItem(){
+ initTimer();
+}
+
+FileFilterItem::FileFilterItem(const QString &directory, const QStringList &filters)
{
+ setDirectory(directory);
+ setFilters(filters);
+ initTimer();
+}
+
+void FileFilterItem::initTimer(){
m_updateFileListTimer.setSingleShot(true);
m_updateFileListTimer.setInterval(50);
- connect(&m_updateFileListTimer, &QTimer::timeout, this, &FileFilterBaseItem::updateFileListNow);
+ connect(&m_updateFileListTimer, &QTimer::timeout, this, &FileFilterItem::updateFileListNow);
}
-Utils::FileSystemWatcher *FileFilterBaseItem::dirWatcher()
+Utils::FileSystemWatcher *FileFilterItem::dirWatcher()
{
if (!m_dirWatcher) {
m_dirWatcher = new Utils::FileSystemWatcher(1, this); // Separate id, might exceed OS limits.
m_dirWatcher->setObjectName(QLatin1String("FileFilterBaseItemWatcher"));
connect(m_dirWatcher, &Utils::FileSystemWatcher::directoryChanged,
- this, &FileFilterBaseItem::updateFileList);
+ this, &FileFilterItem::updateFileList);
}
return m_dirWatcher;
}
-QStringList FileFilterBaseItem::watchedDirectories() const
+QStringList FileFilterItem::watchedDirectories() const
{
return m_dirWatcher ? m_dirWatcher->directories() : QStringList();
}
-QString FileFilterBaseItem::directory() const
+QString FileFilterItem::directory() const
{
return m_rootDir;
}
-void FileFilterBaseItem::setDirectory(const QString &dirPath)
+void FileFilterItem::setDirectory(const QString &dirPath)
{
if (m_rootDir == dirPath)
return;
@@ -54,7 +64,7 @@ void FileFilterBaseItem::setDirectory(const QString &dirPath)
updateFileList();
}
-void FileFilterBaseItem::setDefaultDirectory(const QString &dirPath)
+void FileFilterItem::setDefaultDirectory(const QString &dirPath)
{
if (m_defaultDir == dirPath)
return;
@@ -63,12 +73,12 @@ void FileFilterBaseItem::setDefaultDirectory(const QString &dirPath)
updateFileListNow();
}
-QString FileFilterBaseItem::filter() const
+QStringList FileFilterItem::filters() const
{
return m_filter;
}
-void FileFilterBaseItem::setFilter(const QString &filter)
+void FileFilterItem::setFilters(const QStringList &filter)
{
if (filter == m_filter)
return;
@@ -77,7 +87,7 @@ void FileFilterBaseItem::setFilter(const QString &filter)
m_regExpList.clear();
m_fileSuffixes.clear();
- for (const QString &pattern : filter.split(QLatin1Char(';'))) {
+ for (const QString &pattern : filter) {
if (pattern.isEmpty())
continue;
// decide if it's a canonical pattern like *.x
@@ -96,7 +106,7 @@ void FileFilterBaseItem::setFilter(const QString &filter)
updateFileList();
}
-bool FileFilterBaseItem::recursive() const
+bool FileFilterItem::recursive() const
{
bool recursive;
if (m_recurse == Recurse) {
@@ -112,7 +122,7 @@ bool FileFilterBaseItem::recursive() const
return recursive;
}
-void FileFilterBaseItem::setRecursive(bool recurse)
+void FileFilterItem::setRecursive(bool recurse)
{
bool oldRecursive = recursive();
@@ -125,18 +135,18 @@ void FileFilterBaseItem::setRecursive(bool recurse)
updateFileList();
}
-QStringList FileFilterBaseItem::pathsProperty() const
+QStringList FileFilterItem::pathsProperty() const
{
return m_explicitFiles;
}
-void FileFilterBaseItem::setPathsProperty(const QStringList &path)
+void FileFilterItem::setPathsProperty(const QStringList &path)
{
m_explicitFiles = path;
updateFileList();
}
-QStringList FileFilterBaseItem::files() const
+QStringList FileFilterItem::files() const
{
return Utils::toList(m_files);
}
@@ -146,7 +156,7 @@ QStringList FileFilterBaseItem::files() const
@param filePath: absolute file path
*/
-bool FileFilterBaseItem::matchesFile(const QString &filePath) const
+bool FileFilterItem::matchesFile(const QString &filePath) const
{
for (const QString &explicitFile : m_explicitFiles) {
if (absolutePath(explicitFile) == filePath)
@@ -167,14 +177,14 @@ bool FileFilterBaseItem::matchesFile(const QString &filePath) const
return false;
}
-QString FileFilterBaseItem::absolutePath(const QString &path) const
+QString FileFilterItem::absolutePath(const QString &path) const
{
if (QFileInfo(path).isAbsolute())
return path;
return QDir(absoluteDir()).absoluteFilePath(path);
}
-QString FileFilterBaseItem::absoluteDir() const
+QString FileFilterItem::absoluteDir() const
{
QString absoluteDir;
if (QFileInfo(m_rootDir).isAbsolute())
@@ -185,13 +195,15 @@ QString FileFilterBaseItem::absoluteDir() const
return QDir::cleanPath(absoluteDir);
}
-void FileFilterBaseItem::updateFileList()
+void FileFilterItem::updateFileList()
{
+#ifndef TESTS_ENABLED_QMLPROJECTITEM
if (!m_updateFileListTimer.isActive())
m_updateFileListTimer.start();
+#endif
}
-void FileFilterBaseItem::updateFileListNow()
+void FileFilterItem::updateFileListNow()
{
if (m_updateFileListTimer.isActive())
m_updateFileListTimer.stop();
@@ -233,7 +245,7 @@ void FileFilterBaseItem::updateFileListNow()
dirWatcher()->addDirectories(Utils::toList(watchDirs), Utils::FileSystemWatcher::WatchAllChanges);
}
-bool FileFilterBaseItem::fileMatches(const QString &fileName) const
+bool FileFilterItem::fileMatches(const QString &fileName) const
{
for (const QString &suffix : std::as_const(m_fileSuffixes)) {
if (fileName.endsWith(suffix, Qt::CaseInsensitive))
@@ -248,7 +260,7 @@ bool FileFilterBaseItem::fileMatches(const QString &fileName) const
return false;
}
-QSet<QString> FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir &dir, QSet<QString> *parsedDirs)
+QSet<QString> FileFilterItem::filesInSubTree(const QDir &rootDir, const QDir &dir, QSet<QString> *parsedDirs)
{
QSet<QString> fileSet;
@@ -270,22 +282,5 @@ QSet<QString> FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir
return fileSet;
}
-ImageFileFilterItem::ImageFileFilterItem()
-{
- QString filter;
- // supported image formats according to
- QList<QByteArray> extensions = QImageReader::supportedImageFormats();
- extensions.append("hdr");
- extensions.append("ktx");
- for (const QByteArray &extension : std::as_const(extensions))
- filter.append(QString::fromLatin1("*.%1;").arg(QString::fromLatin1(extension)));
- setFilter(filter);
-}
-
-FileFilterItem::FileFilterItem(const QString &fileFilter)
-{
- setFilter(fileFilter);
-}
-
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h
index 20f6e83718c..e5e3edb2a32 100644
--- a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.h
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h
@@ -7,6 +7,7 @@
#include <QRegularExpression>
#include <QSet>
#include <QTimer>
+#include <QJsonArray>
QT_FORWARD_DECLARE_CLASS(QDir)
@@ -14,16 +15,10 @@ namespace Utils { class FileSystemWatcher; }
namespace QmlProjectManager {
-class QmlProjectContentItem : public QObject
-{
- // base class for all elements that should be direct children of Project element
- Q_OBJECT
+using FileFilterItemPtr = std::unique_ptr<class FileFilterItem>;
+using FileFilterItems = std::vector<FileFilterItemPtr>;
-public:
- QmlProjectContentItem() {}
-};
-
-class FileFilterBaseItem : public QmlProjectContentItem {
+class FileFilterItem : public QObject {
Q_OBJECT
Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged)
@@ -33,15 +28,16 @@ class FileFilterBaseItem : public QmlProjectContentItem {
Q_PROPERTY(QStringList files READ files NOTIFY filesChanged DESIGNABLE false)
public:
- FileFilterBaseItem();
+ FileFilterItem();
+ FileFilterItem(const QString &directory, const QStringList &filters);
QString directory() const;
void setDirectory(const QString &directoryPath);
void setDefaultDirectory(const QString &directoryPath);
- QString filter() const;
- void setFilter(const QString &filter);
+ QStringList filters() const;
+ void setFilters(const QStringList &filter);
bool recursive() const;
void setRecursive(bool recursive);
@@ -73,7 +69,7 @@ private:
QString m_rootDir;
QString m_defaultDir;
- QString m_filter;
+ QStringList m_filter;
// simple "*.png" patterns are stored in m_fileSuffixes, otherwise store in m_regExpList
QList<QString> m_fileSuffixes;
QList<QRegularExpression> m_regExpList;
@@ -93,16 +89,7 @@ private:
QTimer m_updateFileListTimer;
friend class ProjectItem;
-};
-
-class FileFilterItem : public FileFilterBaseItem {
-public:
- FileFilterItem(const QString &fileFilter);
-};
-
-class ImageFileFilterItem : public FileFilterBaseItem {
-public:
- ImageFileFilterItem();
+ void initTimer();
};
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
new file mode 100644
index 00000000000..5b56c8f37ca
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp
@@ -0,0 +1,402 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "qmlprojectitem.h"
+
+#include <QDir>
+#include <QJsonDocument>
+
+#include "converters.h"
+
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
+#include <qmljs/qmljssimplereader.h>
+
+#include <QJsonArray>
+
+namespace QmlProjectManager {
+
+//#define REWRITE_PROJECT_FILE_IN_JSON_FORMAT
+
+QmlProjectItem::QmlProjectItem(const Utils::FilePath &filePath, const bool skipRewrite)
+ : m_projectFile(filePath)
+ , m_skipRewrite(skipRewrite)
+{
+ if (initProjectObject())
+ setupFileFilters();
+}
+
+bool QmlProjectItem::initProjectObject()
+{
+ auto contents = m_projectFile.fileContents();
+ if (!contents) {
+ qWarning() << "Cannot open project file. Path:" << m_projectFile.fileName();
+ return false;
+ }
+
+ QString fileContent{QString::fromUtf8(contents.value())};
+ QJsonObject rootObj;
+ QJsonParseError parseError;
+
+ if (fileContent.contains("import qmlproject", Qt::CaseInsensitive)) {
+ rootObj = Converters::qmlProjectTojson(m_projectFile);
+#ifdef REWRITE_PROJECT_FILE_IN_JSON_FORMAT
+ m_projectFile.writeFileContents(QJsonDocument(rootObj).toJson());
+#endif
+ } else {
+ rootObj
+ = QJsonDocument::fromJson(m_projectFile.fileContents()->data(), &parseError).object();
+ }
+
+ if (rootObj.isEmpty()) {
+ if (parseError.error != QJsonParseError::NoError) {
+ qWarning() << "Cannot parse the json formatted project file. Error:"
+ << parseError.errorString();
+ } else {
+ qWarning() << "Cannot convert QmlProject to Json.";
+ }
+ return false;
+ }
+
+ m_project = rootObj;
+ return true;
+}
+
+void QmlProjectItem::setupFileFilters()
+{
+ auto setupFileFilterItem = [this](const QJsonObject &fileGroup) {
+ for (const QString &directory : fileGroup["directories"].toVariant().toStringList()) {
+ std::unique_ptr<FileFilterItem> fileFilterItem{new FileFilterItem};
+
+ QStringList filesArr;
+ for (const QJsonValue &file : fileGroup["files"].toArray()) {
+ filesArr.append(file["name"].toString());
+ }
+
+ fileFilterItem->setDirectory(directory);
+ fileFilterItem->setFilters(fileGroup["filters"].toVariant().toStringList());
+ fileFilterItem->setRecursive(fileGroup["recursive"].toBool(true));
+ fileFilterItem->setPathsProperty(fileGroup["directories"].toVariant().toStringList());
+ fileFilterItem->setPathsProperty(filesArr);
+ fileFilterItem->setDefaultDirectory(m_projectFile.parentDir().toString());
+#ifndef TESTS_ENABLED_QMLPROJECTITEM
+ connect(fileFilterItem.get(),
+ &FileFilterItem::filesChanged,
+ this,
+ &QmlProjectItem::qmlFilesChanged);
+#endif
+ m_content.push_back(std::move(fileFilterItem));
+ };
+ };
+
+ QJsonObject fileGroups = m_project["fileGroups"].toObject();
+ for (const QString &groupName : fileGroups.keys()) {
+ setupFileFilterItem(fileGroups[groupName].toObject());
+ }
+}
+
+Utils::FilePath QmlProjectItem::sourceDirectory() const
+{
+ return m_projectFile.parentDir();
+}
+
+QString QmlProjectItem::targetDirectory() const
+{
+ return m_project["deployment"].toObject()["targetDirectory"].toString();
+}
+
+bool QmlProjectItem::isQt4McuProject() const
+{
+ return m_project["mcuConfig"].toObject()["mcuEnabled"].toBool();
+}
+
+Utils::EnvironmentItems QmlProjectItem::environment() const
+{
+ Utils::EnvironmentItems envItems;
+ QJsonObject envVariables = m_project["environment"].toObject();
+ foreach (const QString &variableName, envVariables.keys()) {
+ envItems.append(Utils::EnvironmentItem(variableName, envVariables[variableName].toString()));
+ }
+ return envItems;
+}
+
+void QmlProjectItem::addToEnviroment(const QString &key, const QString &value)
+{
+ QJsonObject envVariables = m_project["environment"].toObject();
+ envVariables.insert(key, value);
+ insertAndUpdateProjectFile("environment", envVariables);
+}
+
+QJsonObject QmlProjectItem::project() const
+{
+ return m_project;
+}
+
+QString QmlProjectItem::versionQt() const
+{
+ return m_project["versions"].toObject()["qt"].toString();
+}
+
+void QmlProjectItem::setVersionQt(const QString &version)
+{
+ QJsonObject targetObj = m_project["versions"].toObject();
+ targetObj["qt"] = version;
+ insertAndUpdateProjectFile("versions", targetObj);
+}
+
+QString QmlProjectItem::versionQtQuick() const
+{
+ return m_project["versions"].toObject()["qtQuick"].toString();
+}
+
+void QmlProjectItem::setVersionQtQuick(const QString &version)
+{
+ QJsonObject targetObj = m_project["versions"].toObject();
+ targetObj["qtQuick"] = version;
+ insertAndUpdateProjectFile("versions", targetObj);
+}
+
+QString QmlProjectItem::versionDesignStudio() const
+{
+ return m_project["versions"].toObject()["designStudio"].toString();
+}
+
+void QmlProjectItem::setVersionDesignStudio(const QString &version)
+{
+ QJsonObject targetObj = m_project["versions"].toObject();
+ targetObj["designStudio"] = version;
+ insertAndUpdateProjectFile("versions", targetObj);
+}
+
+QStringList QmlProjectItem::importPaths() const
+{
+ return m_project["importPaths"].toVariant().toStringList();
+}
+
+void QmlProjectItem::setImportPaths(const QStringList &importPaths)
+{
+ insertAndUpdateProjectFile("importPaths", QJsonArray::fromStringList(importPaths));
+}
+
+void QmlProjectItem::addImportPath(const QString &importPath)
+{
+ QJsonArray importPaths = m_project["importPaths"].toArray();
+
+ if (importPaths.contains(importPath))
+ return;
+
+ importPaths.append(importPath);
+ insertAndUpdateProjectFile("importPaths", importPaths);
+}
+
+QStringList QmlProjectItem::fileSelectors() const
+{
+ return m_project["runConfig"].toObject()["fileSelectors"].toVariant().toStringList();
+}
+
+void QmlProjectItem::setFileSelectors(const QStringList &selectors)
+{
+ QJsonObject targetObj = m_project["runConfig"].toObject();
+ targetObj["fileSelectors"] = QJsonArray::fromStringList(selectors);
+ insertAndUpdateProjectFile("runConfig", targetObj);
+}
+
+void QmlProjectItem::addFileSelector(const QString &selector)
+{
+ QJsonObject targetObj = m_project["runConfig"].toObject();
+ QJsonArray fileSelectors = targetObj["fileSelectors"].toArray();
+
+ if (fileSelectors.contains(selector))
+ return;
+
+ fileSelectors.append(selector);
+ targetObj["fileSelectors"] = fileSelectors;
+ insertAndUpdateProjectFile("runConfig", targetObj);
+}
+
+bool QmlProjectItem::multilanguageSupport() const
+{
+ return m_project["language"].toObject()["multiLanguageSupport"].toBool();
+}
+
+void QmlProjectItem::setMultilanguageSupport(const bool &isEnabled)
+{
+ QJsonObject targetObj = m_project["language"].toObject();
+ targetObj["multiLanguageSupport"] = isEnabled;
+ insertAndUpdateProjectFile("language", targetObj);
+}
+
+QStringList QmlProjectItem::supportedLanguages() const
+{
+ return m_project["language"].toObject()["supportedLanguages"].toVariant().toStringList();
+}
+
+void QmlProjectItem::setSupportedLanguages(const QStringList &languages)
+{
+ QJsonObject targetObj = m_project["language"].toObject();
+ targetObj["supportedLanguages"] = QJsonArray::fromStringList(languages);
+ insertAndUpdateProjectFile("language", targetObj);
+}
+
+void QmlProjectItem::addSupportedLanguage(const QString &language)
+{
+ QJsonObject targetObj = m_project["language"].toObject();
+ QJsonArray suppLangs = targetObj["supportedLanguages"].toArray();
+
+ if (suppLangs.contains(language))
+ return;
+
+ suppLangs.append(language);
+ targetObj["supportedLanguages"] = suppLangs;
+ insertAndUpdateProjectFile("language", targetObj);
+}
+
+QString QmlProjectItem::primaryLanguage() const
+{
+ return m_project["language"].toObject()["primaryLanguage"].toString();
+}
+
+void QmlProjectItem::setPrimaryLanguage(const QString &language)
+{
+ QJsonObject targetObj = m_project["language"].toObject();
+ targetObj["primaryLanguage"] = language;
+ insertAndUpdateProjectFile("language", targetObj);
+}
+
+Utils::FilePaths QmlProjectItem::files() const
+{
+ QSet<QString> filesSet;
+ for (const auto &fileFilter : m_content) {
+ const QStringList fileList = fileFilter->files();
+ for (const QString &file : fileList) {
+ filesSet.insert(file);
+ }
+ }
+
+ Utils::FilePaths files;
+ for (const auto &fileName : filesSet) {
+ files.append(Utils::FilePath::fromString(fileName));
+ }
+
+ return files;
+}
+
+/**
+ Check whether the project would include a file path
+ - regardless whether the file already exists or not.
+
+ @param filePath: absolute file path to check
+ */
+bool QmlProjectItem::matchesFile(const QString &filePath) const
+{
+ return Utils::contains(m_content, [&filePath](const auto &fileFilter) {
+ return fileFilter->matchesFile(filePath);
+ });
+}
+
+bool QmlProjectItem::forceFreeType() const
+{
+ return m_project["runConfig"].toObject()["forceFreeType"].toBool();
+}
+
+void QmlProjectItem::setForceFreeType(const bool &isForced)
+{
+ QJsonObject runConfig = m_project["runConfig"].toObject();
+ runConfig["forceFreeType"] = isForced;
+ insertAndUpdateProjectFile("runConfig", runConfig);
+}
+
+void QmlProjectItem::setMainFile(const QString &mainFile)
+{
+ QJsonObject runConfig = m_project["runConfig"].toObject();
+ runConfig["mainFile"] = mainFile;
+ insertAndUpdateProjectFile("runConfig", runConfig);
+}
+
+QString QmlProjectItem::mainFile() const
+{
+ return m_project["runConfig"].toObject()["mainFile"].toString();
+}
+
+void QmlProjectItem::setMainUiFile(const QString &mainUiFile)
+{
+ QJsonObject runConfig = m_project["runConfig"].toObject();
+ runConfig["mainUiFile"] = mainUiFile;
+ insertAndUpdateProjectFile("runConfig", runConfig);
+}
+
+QString QmlProjectItem::mainUiFile() const
+{
+ return m_project["runConfig"].toObject()["mainUiFile"].toString();
+}
+
+bool QmlProjectItem::widgetApp() const
+{
+ return m_project["runConfig"].toObject()["widgetApp"].toBool();
+}
+
+void QmlProjectItem::setWidgetApp(const bool &widgetApp)
+{
+ QJsonObject runConfig = m_project["runConfig"].toObject();
+ runConfig["widgetApp"] = widgetApp;
+ insertAndUpdateProjectFile("runConfig", runConfig);
+}
+
+QStringList QmlProjectItem::shaderToolArgs() const
+{
+ return m_project["shaderTool"].toObject()["args"].toVariant().toStringList();
+}
+
+void QmlProjectItem::setShaderToolArgs(const QStringList &args)
+{
+ QJsonObject shaderTool = m_project["shaderTool"].toObject();
+ shaderTool["args"] = QJsonArray::fromStringList(args);
+ insertAndUpdateProjectFile("shaderTool", shaderTool);
+}
+
+void QmlProjectItem::addShaderToolArg(const QString &arg)
+{
+ QJsonObject targetObj = m_project["shaderTool"].toObject();
+ QJsonArray toolArgs = targetObj["args"].toArray();
+
+ if (toolArgs.contains(arg))
+ return;
+
+ toolArgs.append(arg);
+ targetObj["args"] = toolArgs;
+ insertAndUpdateProjectFile("shaderTool", targetObj);
+}
+
+QStringList QmlProjectItem::shaderToolFiles() const
+{
+ return m_project.value("shaderTool").toObject().value("files").toVariant().toStringList();
+}
+
+void QmlProjectItem::setShaderToolFiles(const QStringList &files)
+{
+ QJsonObject shaderTool = m_project["shaderTool"].toObject();
+ shaderTool["files"] = QJsonArray::fromStringList(files);
+ insertAndUpdateProjectFile("shaderTool", shaderTool);
+}
+
+void QmlProjectItem::addShaderToolFile(const QString &file)
+{
+ QJsonObject targetObj = m_project["shaderTool"].toObject();
+ QJsonArray toolArgs = targetObj["files"].toArray();
+
+ if (toolArgs.contains(file))
+ return;
+
+ toolArgs.append(file);
+ targetObj["files"] = toolArgs;
+ insertAndUpdateProjectFile("shaderTool", targetObj);
+}
+
+void QmlProjectItem::insertAndUpdateProjectFile(const QString &key, const QJsonValue &value)
+{
+ m_project[key] = value;
+ if (!m_skipRewrite)
+ m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8());
+}
+
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
new file mode 100644
index 00000000000..83ef5ca0000
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "filefilteritems.h"
+
+#include <utils/environment.h>
+
+#include <QObject>
+#include <QSet>
+#include <QStringList>
+#include <QSharedPointer>
+#include <QJsonObject>
+
+#include <memory>
+#include <vector>
+
+namespace QmlJS {
+class SimpleReaderNode;
+}
+
+namespace QmlProjectManager {
+
+class QmlProjectItem : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProjectItem(const Utils::FilePath &filePath, const bool skipRewrite = false);
+
+ bool isQt4McuProject() const;
+
+
+ QString versionQt() const;
+ void setVersionQt(const QString &version);
+
+ QString versionQtQuick() const;
+ void setVersionQtQuick(const QString &version);
+
+ QString versionDesignStudio() const;
+ void setVersionDesignStudio(const QString &version);
+
+ Utils::FilePath sourceDirectory() const;
+ QString targetDirectory() const;
+
+ QStringList importPaths() const;
+ void setImportPaths(const QStringList &paths);
+ void addImportPath(const QString &importPath);
+
+ QStringList fileSelectors() const;
+ void setFileSelectors(const QStringList &selectors);
+ void addFileSelector(const QString &selector);
+
+ bool multilanguageSupport() const;
+ void setMultilanguageSupport(const bool &isEnabled);
+
+ QStringList supportedLanguages() const;
+ void setSupportedLanguages(const QStringList &languages);
+ void addSupportedLanguage(const QString &language);
+
+ QString primaryLanguage() const;
+ void setPrimaryLanguage(const QString &language);
+
+ Utils::FilePaths files() const;
+ bool matchesFile(const QString &filePath) const;
+
+ bool forceFreeType() const;
+ void setForceFreeType(const bool &isForced);
+
+ void setMainFile(const QString &mainFile);
+ QString mainFile() const;
+
+ void setMainUiFile(const QString &mainUiFile);
+ QString mainUiFile() const;
+
+ bool widgetApp() const;
+ void setWidgetApp(const bool &widgetApp);
+
+ QStringList shaderToolArgs() const;
+ void setShaderToolArgs(const QStringList &args);
+ void addShaderToolArg(const QString &arg);
+
+ QStringList shaderToolFiles() const;
+ void setShaderToolFiles(const QStringList &files);
+ void addShaderToolFile(const QString &file);
+
+ Utils::EnvironmentItems environment() const;
+ void addToEnviroment(const QString &key, const QString &value);
+
+ QJsonObject project() const;
+
+signals:
+ void qmlFilesChanged(const QSet<QString> &, const QSet<QString> &);
+
+private:
+ typedef QSharedPointer<QmlProjectItem> ShrdPtrQPI;
+ typedef std::unique_ptr<FileFilterItem> UnqPtrFFBI;
+ typedef std::unique_ptr<FileFilterItem> UnqPtrFFI;
+
+ // files & props
+ std::vector<std::unique_ptr<FileFilterItem>> m_content; // content property
+
+ // runtime variables
+ Utils::FilePath m_projectFile; // design studio project file
+ QJsonObject m_project; // root project object
+ const bool m_skipRewrite;
+
+ // initializing functions
+ bool initProjectObject();
+ void setupFileFilters();
+
+ // file update functions
+ void insertAndUpdateProjectFile(const QString &key, const QJsonValue &value);
+};
+
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.cpp
index a561758acfa..78b03ea7bbd 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.cpp
@@ -10,8 +10,7 @@
using namespace ProjectExplorer;
-namespace QmlProjectManager {
-namespace Internal {
+namespace QmlProjectManager::Internal {
QmlProjectNode::QmlProjectNode(Project *project)
: ProjectNode(project->projectDirectory())
@@ -21,5 +20,4 @@ QmlProjectNode::QmlProjectNode(Project *project)
setIcon(DirectoryIcon(":/projectexplorer/images/fileoverlay_qml.png"));
}
-} // namespace Internal
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.h b/src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.h
index 5f676cf432b..5f676cf432b 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectnodes.h
+++ b/src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.h
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
new file mode 100644
index 00000000000..86645dc8c50
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
@@ -0,0 +1,598 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "qmlbuildsystem.h"
+#include "qmlprojectconstants.h"
+
+#include <QtCore5Compat/qtextcodec.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
+
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/documentmanager.h>
+#include <coreplugin/editormanager/documentmodel.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/editormanager/ieditor.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/messagemanager.h>
+
+#include <extensionsystem/iplugin.h>
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
+
+#include <projectexplorer/deploymentdata.h>
+#include <projectexplorer/devicesupport/idevice.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/target.h>
+
+#include "projectitem/qmlprojectitem.h"
+#include "projectnode/qmlprojectnodes.h"
+
+#include "utils/algorithm.h"
+#include "utils/qtcassert.h"
+
+#include "texteditor/textdocument.h"
+
+using namespace ProjectExplorer;
+namespace QmlProjectManager {
+
+namespace {
+Q_LOGGING_CATEGORY(infoLogger, "QmlProjectManager.QmlBuildSystem", QtInfoMsg)
+}
+
+ExtensionSystem::IPlugin *findMcuSupportPlugin()
+{
+ const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault(
+ ExtensionSystem::PluginManager::plugins(),
+ Utils::equal(&ExtensionSystem::PluginSpec::name, QString("McuSupport")));
+
+ if (pluginSpec)
+ return pluginSpec->plugin();
+ return nullptr;
+}
+
+void updateMcuBuildStep(Target *target, bool mcuEnabled)
+{
+ if (auto plugin = findMcuSupportPlugin()) {
+ QMetaObject::invokeMethod(
+ plugin,
+ "updateDeployStep",
+ Qt::DirectConnection,
+ Q_ARG(ProjectExplorer::Target*, target),
+ Q_ARG(bool, mcuEnabled));
+ } else if (mcuEnabled) {
+ qWarning() << "Failed to find McuSupport plugin but qtForMCUs is enabled in the project";
+ }
+}
+
+QmlBuildSystem::QmlBuildSystem(Target *target)
+ : BuildSystem(target)
+{
+ // refresh first - project information is used e.g. to decide the default RC's
+ refresh(RefreshOptions::Project);
+
+ updateDeploymentData();
+ registerMenuButtons();
+
+ connect(target->project(), &Project::activeTargetChanged, [this](Target *target) {
+ refresh(RefreshOptions::NoFileRefresh);
+ updateMcuBuildStep(target, qtForMCUs());
+ });
+ connect(target->project(), &Project::projectFileIsDirty, [this]() {
+ refresh(RefreshOptions::Project);
+ updateMcuBuildStep(project()->activeTarget(), qtForMCUs());
+ });
+
+ // FIXME: Check. Probably bogus after the BuildSystem move.
+ // // addedTarget calls updateEnabled on the runconfigurations
+ // // which needs to happen after refresh
+ // foreach (Target *t, targets())
+ // addedTarget(t);
+}
+
+void QmlBuildSystem::updateDeploymentData()
+{
+ if (!m_projectItem)
+ return;
+
+ if (DeviceTypeKitAspect::deviceTypeId(kit())
+ == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
+ return;
+ }
+
+ ProjectExplorer::DeploymentData deploymentData;
+ for (const auto &file : m_projectItem->files()) {
+ deploymentData.addFile(file, targetFile(file).parentDir().toString());
+ }
+
+ setDeploymentData(deploymentData);
+}
+
+void QmlBuildSystem::registerMenuButtons()
+{
+ Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE);
+
+ // QML Project file update button
+ // This button saves the current configuration into the .qmlproject file
+ auto action = new QAction("Update QmlProject File", this);
+ Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ProjectManager");
+ menu->addAction(cmd, Core::Constants::G_FILE_SAVE);
+ QObject::connect(action, &QAction::triggered, this, &QmlBuildSystem::updateProjectFile);
+}
+
+bool QmlBuildSystem::updateProjectFile()
+{
+ qDebug() << "debug#1-mainfilepath" << mainFilePath();
+
+ QFile file(mainFilePath().fileName().append("project-test"));
+ if (file.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
+ qCritical() << "Cannot open Qml Project file for editing!";
+ return false;
+ }
+
+ QTextStream ts(&file);
+ // License
+ ts << "/* "
+ "File generated by Qt Creator"
+ "Copyright (C) 2016 The Qt Company Ltd."
+ "SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH "
+ "Qt-GPL-exception-1.0"
+ "*/"
+ << Qt::endl
+ << Qt::endl;
+
+ // Components
+ ts << "import QmlProject 1.1" << Qt::endl << Qt::endl;
+
+ return true;
+}
+
+void QmlBuildSystem::triggerParsing()
+{
+ refresh(RefreshOptions::Project);
+}
+
+Utils::FilePath QmlBuildSystem::canonicalProjectDir() const
+{
+ return BuildSystem::target()
+ ->project()
+ ->projectFilePath()
+ .canonicalPath()
+ .normalizedPathName()
+ .parentDir();
+}
+
+void QmlBuildSystem::refresh(RefreshOptions options)
+{
+ ParseGuard guard = guardParsingRun();
+ switch (options) {
+ case RefreshOptions::NoFileRefresh:
+ break;
+ case RefreshOptions::Project:
+ initProjectItem();
+ [[fallthrough]];
+ case RefreshOptions::Files:
+ parseProjectFiles();
+ }
+
+ auto modelManager = QmlJS::ModelManagerInterface::instance();
+ if (!modelManager)
+ return;
+
+ QmlJS::ModelManagerInterface::ProjectInfo projectInfo
+ = modelManager->defaultProjectInfoForProject(project(),
+ project()->files(Project::HiddenRccFolders));
+ const QStringList searchPaths = makeAbsolute(canonicalProjectDir(), customImportPaths());
+ for (const QString &searchPath : searchPaths)
+ projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(searchPath),
+ QmlJS::Dialect::Qml);
+
+ modelManager->updateProjectInfo(projectInfo, project());
+
+ guard.markAsSuccess();
+
+ emit projectChanged();
+}
+
+void QmlBuildSystem::initProjectItem()
+{
+ m_projectItem.reset(new QmlProjectItem{projectFilePath()});
+ connect(m_projectItem.get(),
+ &QmlProjectItem::qmlFilesChanged,
+ this,
+ &QmlBuildSystem::refreshFiles);
+}
+
+void QmlBuildSystem::parseProjectFiles()
+{
+ if (auto modelManager = QmlJS::ModelManagerInterface::instance()) {
+ modelManager->updateSourceFiles(m_projectItem->files(), true);
+ }
+
+
+ Utils::FilePath mainFilePath{Utils::FilePath::fromString(m_projectItem->mainFile())};
+ if (!mainFilePath.isEmpty()) {
+ mainFilePath = canonicalProjectDir().resolvePath(m_projectItem->mainFile());
+ Utils::FileReader reader;
+ QString errorMessage;
+ if (!reader.fetch(mainFilePath, &errorMessage)) {
+ Core::MessageManager::writeFlashing(
+ tr("Warning while loading project file %1.").arg(projectFilePath().toUserOutput()));
+ Core::MessageManager::writeSilently(errorMessage);
+ }
+ }
+
+ generateProjectTree();
+}
+
+void QmlBuildSystem::generateProjectTree()
+{
+ auto newRoot = std::make_unique<Internal::QmlProjectNode>(project());
+
+ for (const auto &file : m_projectItem->files()) {
+ const FileType fileType = (file == projectFilePath())
+ ? FileType::Project
+ : FileNode::fileTypeForFileName(file);
+ newRoot->addNestedNode(std::make_unique<FileNode>(file, fileType));
+ }
+ newRoot->addNestedNode(std::make_unique<FileNode>(projectFilePath(), FileType::Project));
+
+ setRootProjectNode(std::move(newRoot));
+ updateDeploymentData();
+}
+
+bool QmlBuildSystem::setFileSettingInProjectFile(const QString &setting,
+ const Utils::FilePath &mainFilePath,
+ const QString &oldFile)
+{
+ // make sure to change it also in the qmlproject file
+ const Utils::FilePath qmlProjectFilePath = project()->projectFilePath();
+ Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath);
+ const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(
+ qmlProjectFilePath);
+ TextEditor::TextDocument *document = nullptr;
+ if (!editors.isEmpty()) {
+ document = qobject_cast<TextEditor::TextDocument *>(editors.first()->document());
+ if (document && document->isModified())
+ if (!Core::DocumentManager::saveDocument(document))
+ return false;
+ }
+
+ QString fileContent;
+ QString error;
+ Utils::TextFileFormat textFileFormat;
+ const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
+ Utils::TextFileFormat::ReadResult readResult = Utils::TextFileFormat::readFile(qmlProjectFilePath,
+ codec,
+ &fileContent,
+ &textFileFormat,
+ &error);
+ if (readResult != Utils::TextFileFormat::ReadSuccess) {
+ qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error;
+ }
+
+ const QString settingQmlCode = setting + ":";
+
+ const Utils::FilePath projectDir = project()->projectFilePath().parentDir();
+ const QString relativePath = mainFilePath.relativeChildPath(projectDir).path();
+
+ if (fileContent.indexOf(settingQmlCode) < 0) {
+ QString addedText = QString("\n %1 \"%2\"\n").arg(settingQmlCode).arg(relativePath);
+ auto index = fileContent.lastIndexOf("}");
+ fileContent.insert(index, addedText);
+ } else {
+ QString originalFileName = oldFile;
+ originalFileName.replace(".", "\\.");
+ const QRegularExpression expression(
+ QString("%1\\s*\"(%2)\"").arg(settingQmlCode).arg(originalFileName));
+
+ const QRegularExpressionMatch match = expression.match(fileContent);
+
+ fileContent.replace(match.capturedStart(1), match.capturedLength(1), relativePath);
+ }
+
+ if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error))
+ qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error;
+
+ refresh(RefreshOptions::Project);
+ return true;
+}
+
+bool QmlBuildSystem::blockFilesUpdate() const
+{
+ return m_blockFilesUpdate;
+}
+
+void QmlBuildSystem::setBlockFilesUpdate(bool newBlockFilesUpdate)
+{
+ m_blockFilesUpdate = newBlockFilesUpdate;
+}
+
+Utils::FilePath QmlBuildSystem::mainFilePath() const
+{
+ return projectDirectory().pathAppended(mainFile());
+}
+
+Utils::FilePath QmlBuildSystem::mainUiFilePath() const
+{
+ return projectDirectory().pathAppended(mainUiFile());
+}
+
+bool QmlBuildSystem::setMainFileInProjectFile(const Utils::FilePath &newMainFilePath)
+{
+ return setFileSettingInProjectFile("mainFile", newMainFilePath, mainFile());
+}
+
+bool QmlBuildSystem::setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath)
+{
+ return setMainUiFileInMainFile(newMainUiFilePath)
+ && setFileSettingInProjectFile("mainUiFile", newMainUiFilePath, m_projectItem->mainUiFile());
+}
+
+bool QmlBuildSystem::setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath)
+{
+ Core::FileChangeBlocker fileChangeBlocker(mainFilePath());
+ const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(mainFilePath());
+ TextEditor::TextDocument *document = nullptr;
+ if (!editors.isEmpty()) {
+ document = qobject_cast<TextEditor::TextDocument *>(editors.first()->document());
+ if (document && document->isModified())
+ if (!Core::DocumentManager::saveDocument(document))
+ return false;
+ }
+
+ QString fileContent;
+ QString error;
+ Utils::TextFileFormat textFileFormat;
+ const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
+ if (Utils::TextFileFormat::readFile(mainFilePath(), codec, &fileContent, &textFileFormat, &error)
+ != Utils::TextFileFormat::ReadSuccess) {
+ qWarning() << "Failed to read file" << mainFilePath() << ":" << error;
+ }
+
+ const QString currentMain = QString("%1 {").arg(mainUiFilePath().baseName());
+ const QString newMain = QString("%1 {").arg(newMainUiFilePath.baseName());
+
+ if (fileContent.contains(currentMain))
+ fileContent.replace(currentMain, newMain);
+
+ if (!textFileFormat.writeFile(mainFilePath(), fileContent, &error))
+ qWarning() << "Failed to write file" << mainFilePath() << ":" << error;
+
+ return true;
+}
+
+Utils::FilePath QmlBuildSystem::targetDirectory() const
+{
+ if (DeviceTypeKitAspect::deviceTypeId(kit()) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
+ return canonicalProjectDir();
+
+ return m_projectItem ? Utils::FilePath::fromString(m_projectItem->targetDirectory())
+ : Utils::FilePath();
+}
+
+Utils::FilePath QmlBuildSystem::targetFile(const Utils::FilePath &sourceFile) const
+{
+ const QDir sourceDir(m_projectItem ? m_projectItem->sourceDirectory().path()
+ : canonicalProjectDir().toString());
+ const QDir targetDir(targetDirectory().toString());
+ const QString relative = sourceDir.relativeFilePath(sourceFile.toString());
+ return Utils::FilePath::fromString(QDir::cleanPath(targetDir.absoluteFilePath(relative)));
+}
+
+void QmlBuildSystem::setSupportedLanguages(QStringList languages)
+{
+ m_projectItem->setSupportedLanguages(languages);
+}
+
+void QmlBuildSystem::setPrimaryLanguage(QString language)
+{
+ m_projectItem->setPrimaryLanguage(language);
+}
+
+QStringList QmlBuildSystem::makeAbsolute(const Utils::FilePath &path,
+ const QStringList &relativePaths)
+{
+ if (path.isEmpty())
+ return relativePaths;
+
+ const QDir baseDir(path.toString());
+ return Utils::transform(relativePaths, [&baseDir](const QString &path) {
+ return QDir::cleanPath(baseDir.absoluteFilePath(path));
+ });
+}
+
+void QmlBuildSystem::refreshFiles(const QSet<QString> & /*added*/, const QSet<QString> &removed)
+{
+ if (m_blockFilesUpdate) {
+ qCDebug(infoLogger) << "Auto files refresh blocked.";
+ return;
+ }
+ refresh(RefreshOptions::Files);
+ if (!removed.isEmpty()) {
+ if (auto modelManager = QmlJS::ModelManagerInterface::instance()) {
+ modelManager->removeFiles(
+ Utils::transform<QList<Utils::FilePath>>(removed, [](const QString &s) {
+ return Utils::FilePath::fromString(s);
+ }));
+ }
+ }
+ updateDeploymentData();
+}
+
+QVariant QmlBuildSystem::additionalData(Utils::Id id) const
+{
+ if (id == Constants::customFileSelectorsData)
+ return customFileSelectors();
+ if (id == Constants::supportedLanguagesData)
+ return supportedLanguages();
+ if (id == Constants::primaryLanguageData)
+ return primaryLanguage();
+ if (id == Constants::customForceFreeTypeData)
+ return forceFreeType();
+ if (id == Constants::customQtForMCUs)
+ return qtForMCUs();
+ if (id == Constants::customQt6Project)
+ return qt6Project();
+ if (id == Constants::mainFilePath)
+ return mainFilePath().toString();
+ if (id == Constants::customImportPaths)
+ return customImportPaths();
+ if (id == Constants::canonicalProjectDir)
+ return canonicalProjectDir().toString();
+ return {};
+}
+
+bool QmlBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
+{
+ if (dynamic_cast<Internal::QmlProjectNode *>(context)) {
+ if (action == AddNewFile || action == EraseFile)
+ return true;
+ QTC_ASSERT(node, return false);
+
+ if (action == Rename && node->asFileNode()) {
+ const FileNode *fileNode = node->asFileNode();
+ QTC_ASSERT(fileNode, return false);
+ return fileNode->fileType() != FileType::Project;
+ }
+ return false;
+ }
+ return BuildSystem::supportsAction(context, action, node);
+}
+
+bool QmlBuildSystem::addFiles(Node *context, const Utils::FilePaths &filePaths, Utils::FilePaths *)
+{
+ if (!dynamic_cast<Internal::QmlProjectNode *>(context))
+ return false;
+
+ Utils::FilePaths toAdd;
+ for (const Utils::FilePath &filePath : filePaths) {
+ if (!m_projectItem->matchesFile(filePath.toString()))
+ toAdd << filePaths;
+ }
+ return toAdd.isEmpty();
+}
+
+bool QmlBuildSystem::deleteFiles(Node *context, const Utils::FilePaths &filePaths)
+{
+ if (dynamic_cast<Internal::QmlProjectNode *>(context))
+ return true;
+
+ return BuildSystem::deleteFiles(context, filePaths);
+}
+
+bool QmlBuildSystem::renameFile(Node *context,
+ const Utils::FilePath &oldFilePath,
+ const Utils::FilePath &newFilePath)
+{
+ if (dynamic_cast<Internal::QmlProjectNode *>(context)) {
+ if (oldFilePath.endsWith(mainFile()))
+ return setMainFileInProjectFile(newFilePath);
+ if (oldFilePath.endsWith(m_projectItem->mainUiFile()))
+ return setMainUiFileInProjectFile(newFilePath);
+ return true;
+ }
+
+ return BuildSystem::renameFile(context, oldFilePath, newFilePath);
+}
+
+QString QmlBuildSystem::mainFile() const
+{
+ return m_projectItem->mainFile();
+}
+
+QString QmlBuildSystem::mainUiFile() const
+{
+ return m_projectItem->mainUiFile();
+}
+
+bool QmlBuildSystem::qtForMCUs() const
+{
+ return m_projectItem->isQt4McuProject();
+}
+
+bool QmlBuildSystem::qt6Project() const
+{
+ return m_projectItem->versionQt() == "6";
+}
+
+Utils::EnvironmentItems QmlBuildSystem::environment() const
+{
+ return m_projectItem->environment();
+}
+
+QStringList QmlBuildSystem::customImportPaths() const
+{
+ return m_projectItem->importPaths();
+}
+
+QStringList QmlBuildSystem::customFileSelectors() const
+{
+ return m_projectItem->fileSelectors();
+}
+
+bool QmlBuildSystem::multilanguageSupport() const
+{
+ return m_projectItem->multilanguageSupport();
+}
+
+QStringList QmlBuildSystem::supportedLanguages() const
+{
+ return m_projectItem->supportedLanguages();
+}
+
+QString QmlBuildSystem::primaryLanguage() const
+{
+ return m_projectItem->primaryLanguage();
+}
+
+bool QmlBuildSystem::forceFreeType() const
+{
+ return m_projectItem->forceFreeType();
+}
+
+bool QmlBuildSystem::widgetApp() const
+{
+ return m_projectItem->widgetApp();
+}
+
+QStringList QmlBuildSystem::shaderToolArgs() const
+{
+ return m_projectItem->shaderToolArgs();
+}
+
+QStringList QmlBuildSystem::shaderToolFiles() const
+{
+ return m_projectItem->shaderToolFiles();
+}
+
+QStringList QmlBuildSystem::importPaths() const
+{
+ return m_projectItem->importPaths();
+}
+
+Utils::FilePaths QmlBuildSystem::files() const
+{
+ return m_projectItem->files();
+}
+
+QString QmlBuildSystem::versionQt() const
+{
+ return m_projectItem->versionQt();
+}
+
+QString QmlBuildSystem::versionQtQuick() const
+{
+ return m_projectItem->versionQtQuick();
+}
+
+QString QmlBuildSystem::versionDesignStudio() const
+{
+ return m_projectItem->versionDesignStudio();
+}
+
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
new file mode 100644
index 00000000000..d275481f537
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
@@ -0,0 +1,120 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../qmlprojectmanager_global.h"
+#include <projectexplorer/buildsystem.h>
+
+namespace QmlProjectManager {
+
+class QmlProject;
+class QmlProjectItem;
+class QmlProjectFile;
+
+class QMLPROJECTMANAGER_EXPORT QmlBuildSystem : public ProjectExplorer::BuildSystem
+{
+ Q_OBJECT
+
+public:
+ explicit QmlBuildSystem(ProjectExplorer::Target *target);
+ ~QmlBuildSystem() = default;
+
+ void triggerParsing() final;
+
+ bool supportsAction(ProjectExplorer::Node *context,
+ ProjectExplorer::ProjectAction action,
+ const ProjectExplorer::Node *node) const override;
+ bool addFiles(ProjectExplorer::Node *context,
+ const Utils::FilePaths &filePaths,
+ Utils::FilePaths *notAdded = nullptr) override;
+ bool deleteFiles(ProjectExplorer::Node *context, const Utils::FilePaths &filePaths) override;
+ bool renameFile(ProjectExplorer::Node *context,
+ const Utils::FilePath &oldFilePath,
+ const Utils::FilePath &newFilePath) override;
+
+ bool updateProjectFile();
+
+ QString name() const override { return QLatin1String("qml"); }
+
+ QmlProject *qmlProject() const;
+
+ QVariant additionalData(Utils::Id id) const override;
+
+ enum class RefreshOptions {
+ NoFileRefresh,
+ Files,
+ Project,
+ };
+
+ void refresh(RefreshOptions options);
+
+ bool setMainFileInProjectFile(const Utils::FilePath &newMainFilePath);
+ bool setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath);
+ bool setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath);
+
+ Utils::FilePath canonicalProjectDir() const;
+ QString mainFile() const;
+ QString mainUiFile() const;
+ Utils::FilePath mainFilePath() const;
+ Utils::FilePath mainUiFilePath() const;
+
+ bool qtForMCUs() const;
+ bool qt6Project() const;
+
+ Utils::FilePath targetDirectory() const;
+ Utils::FilePath targetFile(const Utils::FilePath &sourceFile) const;
+
+ Utils::EnvironmentItems environment() const;
+ QStringList customImportPaths() const;
+ QStringList customFileSelectors() const;
+ bool multilanguageSupport() const;
+ QStringList supportedLanguages() const;
+ void setSupportedLanguages(QStringList languages);
+ QString primaryLanguage() const;
+ void setPrimaryLanguage(QString language);
+ bool forceFreeType() const;
+ bool widgetApp() const;
+ QStringList shaderToolArgs() const;
+ QStringList shaderToolFiles() const;
+ QStringList importPaths() const;
+ Utils::FilePaths files() const;
+
+ QString versionQt() const;
+ QString versionQtQuick() const;
+ QString versionDesignStudio() const;
+
+ bool addFiles(const QStringList &filePaths);
+ void refreshProjectFile();
+
+ static Utils::FilePath activeMainFilePath();
+ static QStringList makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths);
+
+ void refreshFiles(const QSet<QString> &added, const QSet<QString> &removed);
+
+ bool blockFilesUpdate() const;
+ void setBlockFilesUpdate(bool newBlockFilesUpdate);
+
+signals:
+ void projectChanged();
+
+private:
+ bool setFileSettingInProjectFile(const QString &setting,
+ const Utils::FilePath &mainFilePath,
+ const QString &oldFile);
+
+ QSharedPointer<QmlProjectItem> m_projectItem;
+ bool m_blockFilesUpdate = false;
+
+ void initProjectItem();
+ void parseProjectFiles();
+ void generateProjectTree();
+
+ void registerMenuButtons();
+ void updateDeploymentData();
+ friend class FilesUpdateBlocker;
+};
+
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp
deleted file mode 100644
index 27e5809f943..00000000000
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qmlprojectfileformat.h"
-
-#include "filefilteritems.h"
-#include "qmlprojectitem.h"
-#include "../qmlprojectmanagertr.h"
-
-#include <qmljs/qmljssimplereader.h>
-
-#include <utils/filepath.h>
-
-#include <QDebug>
-#include <QVariant>
-
-#include <memory>
-
-using namespace Utils;
-
-enum {
- debug = false
-};
-
-namespace {
-
-std::unique_ptr<QmlProjectManager::FileFilterBaseItem> setupFileFilterItem(
- std::unique_ptr<QmlProjectManager::FileFilterBaseItem> fileFilterItem,
- const QmlJS::SimpleReaderNode::Ptr &node)
-{
- const auto directoryProperty = node->property(QLatin1String("directory"));
- if (directoryProperty.isValid())
- fileFilterItem->setDirectory(directoryProperty.value.toString());
-
- const auto recursiveProperty = node->property(QLatin1String("recursive"));
- if (recursiveProperty.isValid())
- fileFilterItem->setRecursive(recursiveProperty.value.toBool());
-
- const auto pathsProperty = node->property(QLatin1String("paths"));
- if (pathsProperty.isValid())
- fileFilterItem->setPathsProperty(pathsProperty.value.toStringList());
-
- // "paths" and "files" have the same functionality
- const auto filesProperty = node->property(QLatin1String("files"));
- if (filesProperty.isValid())
- fileFilterItem->setPathsProperty(filesProperty.value.toStringList());
-
- const auto filterProperty = node->property(QLatin1String("filter"));
- if (filterProperty.isValid())
- fileFilterItem->setFilter(filterProperty.value.toString());
-
- if (debug)
- qDebug() << "directory:" << directoryProperty.value << "recursive" << recursiveProperty.value
- << "paths" << pathsProperty.value << "files" << filesProperty.value;
- return fileFilterItem;
-}
-
-} //namespace
-
-namespace QmlProjectManager {
-
-std::unique_ptr<QmlProjectItem> QmlProjectFileFormat::parseProjectFile(const Utils::FilePath &fileName,
- QString *errorMessage)
-{
- QmlJS::SimpleReader simpleQmlJSReader;
-
- const QmlJS::SimpleReaderNode::Ptr rootNode = simpleQmlJSReader.readFile(fileName.toString());
-
- if (!simpleQmlJSReader.errors().isEmpty() || !rootNode->isValid()) {
- qWarning() << "unable to parse:" << fileName;
- qWarning() << simpleQmlJSReader.errors();
- if (errorMessage)
- *errorMessage = simpleQmlJSReader.errors().join(QLatin1String(", "));
- return nullptr;
- }
-
- if (rootNode->name() == QLatin1String("Project")) {
- auto projectItem = std::make_unique<QmlProjectItem>();
-
- const auto mainFileProperty = rootNode->property(QLatin1String("mainFile"));
- if (mainFileProperty.isValid())
- projectItem->setMainFile(mainFileProperty.value.toString());
-
- const auto mainUiFileProperty = rootNode->property(QLatin1String("mainUiFile"));
- if (mainUiFileProperty.isValid())
- projectItem->setMainUiFile(mainUiFileProperty.value.toString());
-
- const auto importPathsProperty = rootNode->property(QLatin1String("importPaths"));
- if (importPathsProperty.isValid()) {
- QStringList list = importPathsProperty.value.toStringList();
- list.removeAll(".");
- projectItem->setImportPaths(list);
- }
-
- const auto fileSelectorsProperty = rootNode->property(QLatin1String("fileSelectors"));
- if (fileSelectorsProperty.isValid())
- projectItem->setFileSelectors(fileSelectorsProperty.value.toStringList());
-
- const auto multilanguageSupportProperty = rootNode->property(
- QLatin1String("multilanguageSupport"));
- if (multilanguageSupportProperty.isValid())
- projectItem->setMultilanguageSupport(multilanguageSupportProperty.value.toBool());
-
- const auto languagesProperty = rootNode->property(QLatin1String("supportedLanguages"));
- if (languagesProperty.isValid())
- projectItem->setSupportedLanguages(languagesProperty.value.toStringList());
-
- const auto primaryLanguageProperty = rootNode->property(QLatin1String("primaryLanguage"));
- if (primaryLanguageProperty.isValid())
- projectItem->setPrimaryLanguage(primaryLanguageProperty.value.toString());
-
- const auto forceFreeTypeProperty = rootNode->property("forceFreeType");
- if (forceFreeTypeProperty.isValid())
- projectItem->setForceFreeType(forceFreeTypeProperty.value.toBool());
-
- const auto targetDirectoryPropery = rootNode->property("targetDirectory");
- if (targetDirectoryPropery.isValid())
- projectItem->setTargetDirectory(FilePath::fromSettings(targetDirectoryPropery.value));
-
- const auto qtForMCUProperty = rootNode->property("qtForMCUs");
- if (qtForMCUProperty.isValid() && qtForMCUProperty.value.toBool())
- projectItem->setQtForMCUs(qtForMCUProperty.value.toBool());
-
- const auto qt6ProjectProperty = rootNode->property("qt6Project");
- if (qt6ProjectProperty.isValid() && qt6ProjectProperty.value.toBool())
- projectItem->setQt6Project(qt6ProjectProperty.value.toBool());
-
- const auto widgetAppProperty = rootNode->property("widgetApp");
- if (widgetAppProperty.isValid())
- projectItem->setWidgetApp(widgetAppProperty.value.toBool());
-
- if (debug)
- qDebug() << "importPath:" << importPathsProperty.value << "mainFile:" << mainFileProperty.value;
-
- const QList<QmlJS::SimpleReaderNode::Ptr> childNodes = rootNode->children();
- for (const QmlJS::SimpleReaderNode::Ptr &childNode : childNodes) {
- if (debug)
- qDebug() << "reading type:" << childNode->name();
-
- if (childNode->name() == QLatin1String("QmlFiles")) {
- projectItem->appendContent(
- setupFileFilterItem(std::make_unique<FileFilterItem>("*.qml"), childNode));
- } else if (childNode->name() == QLatin1String("JavaScriptFiles")) {
- projectItem->appendContent(
- setupFileFilterItem(std::make_unique<FileFilterItem>("*.js"), childNode));
- } else if (childNode->name() == QLatin1String("ImageFiles")) {
- projectItem->appendContent(
- setupFileFilterItem(std::make_unique<ImageFileFilterItem>(), childNode));
- } else if (childNode->name() == QLatin1String("CssFiles")) {
- projectItem->appendContent(
- setupFileFilterItem(std::make_unique<FileFilterItem>("*.css"), childNode));
- } else if (childNode->name() == QLatin1String("FontFiles")) {
- projectItem->appendContent(
- setupFileFilterItem(std::make_unique<FileFilterItem>("*.ttf;*.otf"), childNode));
- } else if (childNode->name() == QLatin1String("Files")) {
- projectItem->appendContent(
- setupFileFilterItem(std::make_unique<FileFilterBaseItem>(), childNode));
- } else if (childNode->name() == "Environment") {
- const auto properties = childNode->properties();
- auto i = properties.constBegin();
- while (i != properties.constEnd()) {
- projectItem->addToEnviroment(i.key(), i.value().value.toString());
- ++i;
- }
- } else if (childNode->name() == "ShaderTool") {
- QmlJS::SimpleReaderNode::Property commandLine = childNode->property("args");
- if (commandLine.isValid()) {
- const QStringList quotedArgs = commandLine.value.toString().split('\"');
- QStringList args;
- for (int i = 0; i < quotedArgs.size(); ++i) {
- // Each odd arg in this list is a single quoted argument, which we should
- // not be split further
- if (i % 2 == 0)
- args.append(quotedArgs[i].trimmed().split(' '));
- else
- args.append(quotedArgs[i]);
- }
- args.removeAll({});
- args.append("-o"); // Prepare for adding output file as next arg
- projectItem->setShaderToolArgs(args);
- }
- QmlJS::SimpleReaderNode::Property files = childNode->property("files");
- if (files.isValid())
- projectItem->setShaderToolFiles(files.value.toStringList());
- } else {
- qWarning() << "Unknown type:" << childNode->name();
- }
- }
- return projectItem;
- }
-
- if (errorMessage)
- *errorMessage = Tr::tr("Invalid root element: %1").arg(rootNode->name());
-
- return nullptr;
-}
-
-} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h
deleted file mode 100644
index 72c77999711..00000000000
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <utils/fileutils.h>
-
-#include <QCoreApplication>
-#include <QString>
-
-namespace QmlProjectManager {
-
-class QmlProjectItem;
-
-class QmlProjectFileFormat
-{
-public:
- static std::unique_ptr<QmlProjectItem> parseProjectFile(const Utils::FilePath &fileName,
- QString *errorMessage = nullptr);
-};
-
-} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
deleted file mode 100644
index 3cd7c28af24..00000000000
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qmlprojectitem.h"
-#include "filefilteritems.h"
-
-#include <utils/algorithm.h>
-
-#include <QDir>
-
-using namespace Utils;
-
-namespace QmlProjectManager {
-
-// kind of initialization
-void QmlProjectItem::setSourceDirectory(const FilePath &directoryPath)
-{
- if (m_sourceDirectory == directoryPath)
- return;
-
- m_sourceDirectory = directoryPath;
-
- for (auto &fileFilter : m_content) {
- fileFilter->setDefaultDirectory(directoryPath.toFSPathString());
- connect(fileFilter.get(),
- &FileFilterBaseItem::filesChanged,
- this,
- &QmlProjectItem::qmlFilesChanged);
- }
-}
-
-void QmlProjectItem::setTargetDirectory(const FilePath &directoryPath)
-{
- m_targetDirectory = directoryPath;
-}
-
-void QmlProjectItem::setQtForMCUs(bool b)
-{
- m_qtForMCUs = b;
-}
-
-void QmlProjectItem::setQt6Project(bool qt6Project)
-{
- m_qt6Project = qt6Project;
-}
-
-void QmlProjectItem::setImportPaths(const QStringList &importPaths)
-{
- if (m_importPaths != importPaths)
- m_importPaths = importPaths;
-}
-
-void QmlProjectItem::setFileSelectors(const QStringList &selectors)
-{
- if (m_fileSelectors != selectors)
- m_fileSelectors = selectors;
-}
-
-void QmlProjectItem::setMultilanguageSupport(const bool isEnabled)
-{
- m_multilanguageSupport = isEnabled;
-}
-
-void QmlProjectItem::setSupportedLanguages(const QStringList &languages)
-{
- if (m_supportedLanguages != languages)
- m_supportedLanguages = languages;
-}
-
-void QmlProjectItem::setPrimaryLanguage(const QString &language)
-{
- if (m_primaryLanguage != language)
- m_primaryLanguage = language;
-}
-
-/* Returns list of absolute paths */
-QStringList QmlProjectItem::files() const
-{
- QSet<QString> files;
-
- for (const auto &fileFilter : m_content) {
- const QStringList fileList = fileFilter->files();
- for (const QString &file : fileList) {
- files.insert(file);
- }
- }
- return Utils::toList(files);
-}
-
-/**
- Check whether the project would include a file path
- - regardless whether the file already exists or not.
-
- @param filePath: absolute file path to check
- */
-bool QmlProjectItem::matchesFile(const QString &filePath) const
-{
- return Utils::contains(m_content, [&filePath](const auto &fileFilter) {
- return fileFilter->matchesFile(filePath);
- });
-}
-
-void QmlProjectItem::setForceFreeType(bool b)
-{
- m_forceFreeType = b;
-}
-
-Utils::EnvironmentItems QmlProjectItem::environment() const
-{
- return m_environment;
-}
-
-void QmlProjectItem::addToEnviroment(const QString &key, const QString &value)
-{
- m_environment.append(Utils::EnvironmentItem(key, value));
-}
-
-} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
deleted file mode 100644
index f5ac618bf62..00000000000
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "filefilteritems.h"
-
-#include <utils/environment.h>
-#include <utils/filepath.h>
-
-#include <QObject>
-#include <QSet>
-#include <QStringList>
-
-#include <memory>
-#include <vector>
-
-namespace QmlProjectManager {
-
-class QmlProjectItem : public QObject
-{
- Q_OBJECT
-
-public:
- const Utils::FilePath &sourceDirectory() const { return m_sourceDirectory; }
- void setSourceDirectory(const Utils::FilePath &directoryPath);
- const Utils::FilePath &targetDirectory() const { return m_targetDirectory; }
- void setTargetDirectory(const Utils::FilePath &directoryPath);
-
- bool qtForMCUs() const { return m_qtForMCUs; }
- void setQtForMCUs(bool qtForMCUs);
-
- bool qt6Project() const { return m_qt6Project; }
- void setQt6Project(bool qt6Project);
-
- QStringList importPaths() const { return m_importPaths; }
- void setImportPaths(const QStringList &paths);
-
- QStringList fileSelectors() const { return m_fileSelectors; }
- void setFileSelectors(const QStringList &selectors);
-
- bool multilanguageSupport() const { return m_multilanguageSupport; }
- void setMultilanguageSupport(const bool isEnabled);
-
- QStringList supportedLanguages() const { return m_supportedLanguages; }
- void setSupportedLanguages(const QStringList &languages);
-
- QString primaryLanguage() const { return m_primaryLanguage; }
- void setPrimaryLanguage(const QString &language);
-
- QStringList files() const;
- bool matchesFile(const QString &filePath) const;
-
- bool forceFreeType() const { return m_forceFreeType; };
- void setForceFreeType(bool);
-
- QString mainFile() const { return m_mainFile; }
- void setMainFile(const QString &mainFilePath) { m_mainFile = mainFilePath; }
-
- QString mainUiFile() const { return m_mainUiFile; }
- void setMainUiFile(const QString &mainUiFilePath) { m_mainUiFile = mainUiFilePath; }
-
- bool widgetApp() const { return m_widgetApp; }
- void setWidgetApp(bool widgetApp) { m_widgetApp = widgetApp; }
-
- QStringList shaderToolArgs() const { return m_shaderToolArgs; }
- void setShaderToolArgs(const QStringList &args) {m_shaderToolArgs = args; }
-
- QStringList shaderToolFiles() const { return m_shaderToolFiles; }
- void setShaderToolFiles(const QStringList &files) { m_shaderToolFiles = files; }
-
- void appendContent(std::unique_ptr<FileFilterBaseItem> item)
- {
- m_content.push_back(std::move(item));
- }
-
- Utils::EnvironmentItems environment() const;
- void addToEnviroment(const QString &key, const QString &value);
-
-signals:
- void qmlFilesChanged(const QSet<QString> &, const QSet<QString> &);
-
-protected:
- Utils::FilePath m_sourceDirectory;
- Utils::FilePath m_targetDirectory;
- QStringList m_importPaths;
- QStringList m_fileSelectors;
- bool m_multilanguageSupport;
- QStringList m_supportedLanguages;
- QString m_primaryLanguage;
- QString m_mainFile;
- QString m_mainUiFile;
- Utils::EnvironmentItems m_environment;
- std::vector<std::unique_ptr<FileFilterBaseItem>> m_content; // content property
- bool m_forceFreeType = false;
- bool m_qtForMCUs = false;
- bool m_qt6Project = false;
- bool m_widgetApp = false;
- QStringList m_shaderToolArgs;
- QStringList m_shaderToolFiles;
-};
-
-} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
index 405e63965fe..5d328fd934a 100644
--- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
@@ -181,7 +181,7 @@ void QmlMainFileAspect::setScriptSource(MainScriptSource source, const QString &
FilePath QmlMainFileAspect::mainScript() const
{
if (!qmlBuildSystem()->mainFile().isEmpty()) {
- const FilePath pathInProject = qmlBuildSystem()->mainFile();
+ const FilePath pathInProject = qmlBuildSystem()->mainFilePath();
return qmlBuildSystem()->canonicalProjectDir().resolvePath(pathInProject);
}
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index 77e3cc82d40..ce2f363cd41 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -1,37 +1,27 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "qmlproject.h"
-#include "fileformat/qmlprojectfileformat.h"
-#include "fileformat/qmlprojectitem.h"
-#include "qmlprojectconstants.h"
-#include "qmlprojectmanagerconstants.h"
-#include "qmlprojectmanagertr.h"
-#include "qmlprojectnodes.h"
+#include <qtsupport/baseqtversion.h>
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtsupportconstants.h>
-#include <coreplugin/coreconstants.h>
-#include <coreplugin/documentmanager.h>
-#include <coreplugin/editormanager/documentmodel.h>
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/editormanager/ieditor.h>
-#include <coreplugin/icontext.h>
-#include <coreplugin/icore.h>
-#include <coreplugin/messagemanager.h>
-#include <coreplugin/modemanager.h>
+#include <QTimer>
-#include <projectexplorer/deploymentdata.h>
-#include <projectexplorer/devicesupport/idevice.h>
-#include <projectexplorer/kitinformation.h>
-#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <qtsupport/baseqtversion.h>
-#include <qtsupport/qtkitinformation.h>
-#include <qtsupport/qtsupportconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+#include "projectexplorer/devicesupport/idevice.h"
+#include "qmlprojectconstants.h"
+#include "qmlprojectmanagerconstants.h"
+#include "qmlprojectmanagertr.h"
+#include "utils/algorithm.h"
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <texteditor/textdocument.h>
@@ -50,52 +40,13 @@
using namespace Core;
using namespace ProjectExplorer;
-using namespace QmlProjectManager::Internal;
-using namespace Utils;
-
-namespace {
-Q_LOGGING_CATEGORY(infoLogger, "QmlProjectManager.QmlBuildSystem", QtInfoMsg)
-}
namespace QmlProjectManager {
-
-static int preferedQtTarget(Target *target)
-{
- if (target) {
- const QmlBuildSystem *buildSystem = qobject_cast<QmlBuildSystem *>(target->buildSystem());
- if (buildSystem && buildSystem->qt6Project())
- return 6;
- }
- return 5;
-}
-
-static bool allowOnlySingleProject()
-{
- QSettings *settings = Core::ICore::settings();
- const QString qdsAllowMultipleProjects = "QML/Designer/AllowMultipleProjects";
-
- return !settings->value(qdsAllowMultipleProjects, false).toBool();
-}
-
-Utils::FilePaths QmlProject::collectUiQmlFilesForFolder(const Utils::FilePath &folder) const
-{
- const Utils::FilePaths uiFiles = files([&](const ProjectExplorer::Node *node) {
- return node->filePath().completeSuffix() == "ui.qml"
- && node->filePath().parentDir() == folder;
- });
- return uiFiles;
-}
-
-FilePaths QmlProject::collectQmlFiles() const
-{
- return files([](const Node *node) { return node->filePath().suffix() == "qml"; });
-}
-
QmlProject::QmlProject(const Utils::FilePath &fileName)
: Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName)
{
setId(QmlProjectManager::Constants::QML_PROJECT_ID);
- setProjectLanguages(Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID));
+ setProjectLanguages(Core::Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID));
setDisplayName(fileName.completeBaseName());
setNeedsBuildConfigurations(false);
@@ -106,449 +57,122 @@ QmlProject::QmlProject(const Utils::FilePath &fileName)
EditorManager::closeAllDocuments();
ProjectManager::closeAllProjects();
}
-
- m_openFileConnection
- = connect(this,
- &QmlProject::anyParsingFinished,
- this,
- [this](Target *target, bool success) {
- if (m_openFileConnection)
- disconnect(m_openFileConnection);
-
- if (target && success) {
-
- auto target = activeTarget();
- if (!target)
- return;
-
- auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
- target->buildSystem());
-
- const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath();
-
- if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists()) {
- QTimer::singleShot(1000, [mainUiFile]() {
- Core::EditorManager::openEditor(mainUiFile,
- Utils::Id());
- });
- } else {
- Utils::FilePaths uiFiles = collectUiQmlFilesForFolder(
- projectDirectory().pathAppended("content"));
- if (uiFiles.isEmpty())
- uiFiles = collectUiQmlFilesForFolder(projectDirectory());
-
- if (uiFiles.isEmpty())
- uiFiles = collectQmlFiles();
-
- if (!uiFiles.isEmpty()) {
- Utils::FilePath currentFile;
- if (auto cd = Core::EditorManager::currentDocument())
- currentFile = cd->filePath();
-
- if (currentFile.isEmpty() || !isKnownFile(currentFile))
- QTimer::singleShot(1000, [uiFiles]() {
- Core::EditorManager::openEditor(uiFiles.first(),
- Utils::Id());
- Core::ModeManager::activateMode(
- Core::Constants::MODE_DESIGN);
- });
- }
- }
- }
- });
}
-}
-
-QmlBuildSystem::QmlBuildSystem(Target *target)
- : BuildSystem(target)
-{
- m_canonicalProjectDir =
- target->project()->projectFilePath().canonicalPath().normalizedPathName().parentDir();
-
- connect(target->project(), &Project::projectFileIsDirty,
- this, &QmlBuildSystem::refreshProjectFile);
-
- // refresh first - project information is used e.g. to decide the default RC's
- refresh(Everything);
-// FIXME: Check. Probably bogus after the BuildSystem move.
-// // addedTarget calls updateEnabled on the runconfigurations
-// // which needs to happen after refresh
-// const QLis<Target> targetList = targets();
-// for (Target *t : targetList)
-// addedTarget(t);
-
- connect(target->project(), &Project::activeTargetChanged,
- this, &QmlBuildSystem::onActiveTargetChanged);
- updateDeploymentData();
+ connect(this, &QmlProject::anyParsingFinished, this, &QmlProject::parsingFinished);
}
-QmlBuildSystem::~QmlBuildSystem() = default;
-
-void QmlBuildSystem::triggerParsing()
+void QmlProject::parsingFinished(const Target *target, bool success)
{
- refresh(Everything);
-}
+ // trigger only once
+ disconnect(this, &QmlProject::anyParsingFinished, this, &QmlProject::parsingFinished);
-void QmlBuildSystem::onActiveTargetChanged(Target *)
-{
- // make sure e.g. the default qml imports are adapted
- refresh(Configuration);
-}
+ // FIXME: what to do in this case?
+ if (!target || !success || !activeTarget())
+ return;
-void QmlBuildSystem::onKitChanged()
-{
- // make sure e.g. the default qml imports are adapted
- refresh(Configuration);
-}
+ auto targetActive = activeTarget();
+ auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
+ targetActive->buildSystem());
-Utils::FilePath QmlBuildSystem::canonicalProjectDir() const
-{
- return m_canonicalProjectDir;
-}
+ const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath();
-void QmlBuildSystem::parseProject(RefreshOptions options)
-{
- if (options & Files) {
- if (options & ProjectFile)
- m_projectItem.reset();
- if (!m_projectItem) {
- QString errorMessage;
- m_projectItem = QmlProjectFileFormat::parseProjectFile(projectFilePath(), &errorMessage);
- if (m_projectItem) {
- connect(m_projectItem.get(),
- &QmlProjectItem::qmlFilesChanged,
- this,
- &QmlBuildSystem::refreshFiles);
-
- } else {
- MessageManager::writeFlashing(
- Tr::tr("Error while loading project file %1.").arg(projectFilePath().toUserOutput()));
- MessageManager::writeSilently(errorMessage);
- }
- }
- if (m_projectItem) {
- m_projectItem->setSourceDirectory(canonicalProjectDir());
- if (m_projectItem->targetDirectory().isEmpty())
- m_projectItem->setTargetDirectory(canonicalProjectDir());
-
- if (auto modelManager = QmlJS::ModelManagerInterface::instance()) {
- QStringList files = m_projectItem->files();
- modelManager
- ->updateSourceFiles(Utils::transform(files,
- [](const QString &p) {
- return Utils::FilePath::fromString(p);
- }),
- true);
- }
- QString mainFilePath = m_projectItem->mainFile();
- if (!mainFilePath.isEmpty()) {
- mainFilePath
- = QDir(canonicalProjectDir().toString()).absoluteFilePath(mainFilePath);
- Utils::FileReader reader;
- QString errorMessage;
- if (!reader.fetch(Utils::FilePath::fromString(mainFilePath), &errorMessage)) {
- MessageManager::writeFlashing(Tr::tr("Warning while loading project file %1.")
- .arg(projectFilePath().toUserOutput()));
- MessageManager::writeSilently(errorMessage);
- }
- }
- }
- generateProjectTree();
- }
-
- if (options & Configuration) {
- // update configuration
- }
-}
-
-bool QmlBuildSystem::setFileSettingInProjectFile(const QString &setting,
- const FilePath &mainFilePath,
- const FilePath &oldFile)
-{
- // make sure to change it also in the qmlproject file
- const Utils::FilePath qmlProjectFilePath = project()->projectFilePath();
- Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath);
- const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath);
- TextEditor::TextDocument *document = nullptr;
- if (!editors.isEmpty()) {
- document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document());
- if (document && document->isModified())
- if (!Core::DocumentManager::saveDocument(document))
- return false;
+ if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists()) {
+ QTimer::singleShot(1000, [mainUiFile]() {
+ Core::EditorManager::openEditor(mainUiFile, Utils::Id());
+ });
+ return;
}
- QString fileContent;
- QString error;
- Utils::TextFileFormat textFileFormat;
- const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
- if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error)
- != Utils::TextFileFormat::ReadSuccess) {
- qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error;
+ Utils::FilePaths uiFiles = collectUiQmlFilesForFolder(
+ projectDirectory().pathAppended("content"));
+ if (uiFiles.isEmpty()) {
+ uiFiles = collectUiQmlFilesForFolder(projectDirectory());
+ if (uiFiles.isEmpty())
+ return;
}
- const QString settingQmlCode = setting + ":";
-
- const Utils::FilePath projectDir = project()->projectFilePath().parentDir();
- const QString relativePath = mainFilePath.relativeChildPath(projectDir).path();
-
- if (fileContent.indexOf(settingQmlCode) < 0) {
- QString addedText = QString("\n %1 \"%2\"\n").arg(settingQmlCode).arg(relativePath);
- auto index = fileContent.lastIndexOf("}");
- fileContent.insert(index, addedText);
- } else {
- QString originalFileName = oldFile.path();
- originalFileName.replace(".", "\\.");
- const QRegularExpression expression(QString("%1\\s*\"(%2)\"").arg(settingQmlCode).arg(originalFileName));
-
- const QRegularExpressionMatch match = expression.match(fileContent);
+ Utils::FilePath currentFile;
+ if (auto cd = Core::EditorManager::currentDocument())
+ currentFile = cd->filePath();
- fileContent.replace(match.capturedStart(1),
- match.capturedLength(1),
- relativePath);
+ if (currentFile.isEmpty() || !isKnownFile(currentFile)) {
+ QTimer::singleShot(1000, [uiFiles]() {
+ Core::EditorManager::openEditor(uiFiles.first(), Utils::Id());
+ });
}
-
- if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error))
- qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error;
-
- refresh(Everything);
- return true;
}
-void QmlBuildSystem::refresh(RefreshOptions options)
+Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage)
{
- ParseGuard guard = guardParsingRun();
- parseProject(options);
-
- if (options & Files)
- generateProjectTree();
-
- auto modelManager = QmlJS::ModelManagerInterface::instance();
- if (!modelManager)
- return;
-
- QmlJS::ModelManagerInterface::ProjectInfo projectInfo
- = modelManager->defaultProjectInfoForProject(project(),
- project()->files(Project::HiddenRccFolders));
- const QStringList searchPaths = makeAbsolute(canonicalProjectDir(), customImportPaths());
- for (const QString &searchPath : searchPaths)
- projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(searchPath),
- QmlJS::Dialect::Qml);
-
- modelManager->updateProjectInfo(projectInfo, project());
-
- guard.markAsSuccess();
+ RestoreResult result = Project::fromMap(map, errorMessage);
+ if (result != RestoreResult::Ok)
+ return result;
- emit projectChanged();
-}
+ if (activeTarget())
+ return RestoreResult::Ok;
-FilePath QmlBuildSystem::mainFile() const
-{
- if (m_projectItem)
- return FilePath::fromString(m_projectItem->mainFile());
- return {};
-}
+ // find a kit that matches prerequisites (prefer default one)
+ const QList<Kit *> kits = Utils::filtered(KitManager::kits(), [this](const Kit *k) {
+ return !containsType(projectIssues(k), Task::TaskType::Error)
+ && DeviceTypeKitAspect::deviceTypeId(k)
+ == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
+ });
-FilePath QmlBuildSystem::mainUiFile() const
-{
- if (m_projectItem)
- return FilePath::fromString(m_projectItem->mainUiFile());
- return {};
-}
+ if (!kits.isEmpty()) {
+ if (kits.contains(KitManager::defaultKit()))
+ addTargetForDefaultKit();
+ else
+ addTargetForKit(kits.first());
+ }
-FilePath QmlBuildSystem::mainFilePath() const
-{
- const auto mainFileString = mainFile();
+ // FIXME: are there any other way?
+ // What if it's not a Design Studio project? What should we do then?
+ if (QmlProject::isQtDesignStudio()) {
+ int preferedVersion = preferedQtTarget(activeTarget());
- if (mainFileString.isEmpty())
- return {};
+ setKitWithVersion(preferedVersion, kits);
+ }
- return projectDirectory().resolvePath(mainFileString);
+ return RestoreResult::Ok;
}
-FilePath QmlBuildSystem::mainUiFilePath() const
+bool QmlProject::setKitWithVersion(const int qtMajorVersion, const QList<Kit *> kits)
{
- const auto mainUiFileString = mainUiFile();
-
- if (mainUiFileString.isEmpty())
- return {};
+ const QList<Kit *> qtVersionkits = Utils::filtered(kits, [qtMajorVersion](const Kit *k) {
+ if (!k->isAutoDetected())
+ return false;
- return projectDirectory().resolvePath(mainUiFileString);
-}
+ if (k->isReplacementKit())
+ return false;
-bool QmlBuildSystem::setMainFileInProjectFile(const FilePath &newMainFilePath)
-{
- return setFileSettingInProjectFile("mainFile", newMainFilePath, mainFile());
-}
+ QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k);
+ return (version && version->qtVersion().majorVersion() == qtMajorVersion);
+ });
-bool QmlBuildSystem::setMainUiFileInProjectFile(const FilePath &newMainUiFilePath)
-{
- return setMainUiFileInMainFile(newMainUiFilePath)
- && setFileSettingInProjectFile("mainUiFile", newMainUiFilePath, mainUiFile());
-}
-bool QmlBuildSystem::setMainUiFileInMainFile(const FilePath &newMainUiFilePath)
-{
- Core::FileChangeBlocker fileChangeBlocker(mainFilePath());
- const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(mainFilePath());
- TextEditor::TextDocument *document = nullptr;
- if (!editors.isEmpty()) {
- document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document());
- if (document && document->isModified())
- if (!Core::DocumentManager::saveDocument(document))
- return false;
- }
+ Target *target = nullptr;
- QString fileContent;
- QString error;
- Utils::TextFileFormat textFileFormat;
- const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
- if (Utils::TextFileFormat::readFile(mainFilePath(), codec, &fileContent, &textFileFormat, &error)
- != Utils::TextFileFormat::ReadSuccess) {
- qWarning() << "Failed to read file" << mainFilePath() << ":" << error;
+ if (!qtVersionkits.isEmpty()) {
+ if (qtVersionkits.contains(KitManager::defaultKit()))
+ target = addTargetForDefaultKit();
+ else
+ target = addTargetForKit(qtVersionkits.first());
}
- const QString currentMain = QString("%1 {").arg(mainUiFilePath().baseName());
- const QString newMain = QString("%1 {").arg(newMainUiFilePath.baseName());
-
- if (fileContent.contains(currentMain))
- fileContent.replace(currentMain, newMain);
-
- if (!textFileFormat.writeFile(mainFilePath(), fileContent, &error))
- qWarning() << "Failed to write file" << mainFilePath() << ":" << error;
+ if (target)
+ target->project()->setActiveTarget(target, SetActive::NoCascade);
return true;
}
-bool QmlBuildSystem::qtForMCUs() const
-{
- if (m_projectItem)
- return m_projectItem->qtForMCUs();
- return false;
-}
-
-bool QmlBuildSystem::qt6Project() const
-{
- if (m_projectItem)
- return m_projectItem->qt6Project();
- return false;
-}
-
-void QmlBuildSystem::setMainFile(const QString &mainFilePath)
-{
- if (m_projectItem)
- m_projectItem->setMainFile(mainFilePath);
-}
-
-FilePath QmlBuildSystem::targetDirectory() const
-{
- if (DeviceTypeKitAspect::deviceTypeId(kit())
- == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
- return canonicalProjectDir();
-
- return m_projectItem ? m_projectItem->targetDirectory() : FilePath();
-}
-
-FilePath QmlBuildSystem::targetFile(const FilePath &sourceFile) const
-{
- const FilePath sourceDir = m_projectItem ? m_projectItem->sourceDirectory()
- : canonicalProjectDir();
- const FilePath targetDir = targetDirectory();
- const FilePath relative = sourceFile.relativePathFrom(sourceDir);
- return targetDir.resolvePath(relative);
-}
-
-Utils::EnvironmentItems QmlBuildSystem::environment() const
-{
- if (m_projectItem)
- return m_projectItem->environment();
- return {};
-}
-
-QStringList QmlBuildSystem::customImportPaths() const
-{
- if (m_projectItem)
- return m_projectItem->importPaths();
- return {};
-}
-
-QStringList QmlBuildSystem::customFileSelectors() const
-{
- if (m_projectItem)
- return m_projectItem->fileSelectors();
- return {};
-}
-
-bool QmlBuildSystem::multilanguageSupport() const
-{
- if (m_projectItem)
- return m_projectItem->multilanguageSupport();
- return false;
-}
-
-QStringList QmlBuildSystem::supportedLanguages() const
-{
- if (m_projectItem)
- return m_projectItem->supportedLanguages();
- return {};
-}
-
-void QmlBuildSystem::setSupportedLanguages(QStringList languages)
-{
- if (m_projectItem)
- m_projectItem->setSupportedLanguages(languages);
-}
-
-QString QmlBuildSystem::primaryLanguage() const
-{
- if (m_projectItem)
- return m_projectItem->primaryLanguage();
- return {};
-}
-
-void QmlBuildSystem::setPrimaryLanguage(QString language)
-{
- if (m_projectItem)
- m_projectItem->setPrimaryLanguage(language);
-}
-
-void QmlBuildSystem::refreshProjectFile()
-{
- refresh(QmlBuildSystem::ProjectFile | Files);
-}
-
-QStringList QmlBuildSystem::makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths)
+Utils::FilePaths QmlProject::collectUiQmlFilesForFolder(const Utils::FilePath &folder) const
{
- if (path.isEmpty())
- return relativePaths;
-
- const QDir baseDir(path.toString());
- return Utils::transform(relativePaths, [&baseDir](const QString &path) {
- return QDir::cleanPath(baseDir.absoluteFilePath(path));
+ const Utils::FilePaths uiFiles = files([&](const Node *node) {
+ return node->filePath().completeSuffix() == "ui.qml"
+ && node->filePath().parentDir() == folder;
});
-}
-
-void QmlBuildSystem::refreshFiles(const QSet<QString> &/*added*/, const QSet<QString> &removed)
-{
- if (m_blockFilesUpdate) {
- qCDebug(infoLogger) << "Auto files refresh blocked.";
- return;
- }
- refresh(Files);
- if (!removed.isEmpty()) {
- if (auto modelManager = QmlJS::ModelManagerInterface::instance()) {
- modelManager->removeFiles(
- Utils::transform<QList<Utils::FilePath>>(removed, [](const QString &s) {
- return Utils::FilePath::fromString(s);
- }));
- }
- }
- refreshTargetDirectory();
-}
-
-void QmlBuildSystem::refreshTargetDirectory()
-{
- updateDeploymentData();
+ return uiFiles;
}
Tasks QmlProject::projectIssues(const Kit *k) const
@@ -572,8 +196,8 @@ Tasks QmlProject::projectIssues(const Kit *k) const
if (dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
if (version->type() == QtSupport::Constants::DESKTOPQT) {
if (version->qmlRuntimeFilePath().isEmpty()) {
- result.append(createProjectTask(Task::TaskType::Error,
- Tr::tr("Qt version has no QML utility.")));
+ result.append(
+ createProjectTask(Task::TaskType::Error, tr("Qt version has no QML utility.")));
}
} else {
// Non-desktop Qt on a desktop device? We don't support that.
@@ -589,68 +213,10 @@ Tasks QmlProject::projectIssues(const Kit *k) const
return result;
}
-bool QmlProject::isEditModePreferred() const
-{
- return !isQtDesignStudio();
-}
-
-Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage)
-{
- RestoreResult result = Project::fromMap(map, errorMessage);
- if (result != RestoreResult::Ok)
- return result;
-
- if (!activeTarget()) {
- // find a kit that matches prerequisites (prefer default one)
- const QList<Kit *> kits = Utils::filtered(KitManager::kits(), [this](const Kit *k) {
- return !containsType(projectIssues(k), Task::TaskType::Error)
- && DeviceTypeKitAspect::deviceTypeId(k)
- == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
- });
-
- if (!kits.isEmpty()) {
- if (kits.contains(KitManager::defaultKit()))
- addTargetForDefaultKit();
- else
- addTargetForKit(kits.first());
- }
-
- if (QmlProject::isQtDesignStudio()) {
- auto setKitWithVersion = [&](int qtMajorVersion) {
- const QList<Kit *> qtVersionkits
- = Utils::filtered(kits, [qtMajorVersion](const Kit *k) {
- if (!k->isAutoDetected())
- return false;
- if (k->isReplacementKit())
- return false;
- QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k);
- return (version && version->qtVersion().majorVersion() == qtMajorVersion);
- });
- if (!qtVersionkits.isEmpty()) {
- if (qtVersionkits.contains(KitManager::defaultKit()))
- addTargetForDefaultKit();
- else
- addTargetForKit(qtVersionkits.first());
- }
- };
-
- int preferedVersion = preferedQtTarget(activeTarget());
-
- if (activeTarget())
- removeTarget(activeTarget());
-
- setKitWithVersion(preferedVersion);
- }
- }
-
- return RestoreResult::Ok;
-}
-
bool QmlProject::isQtDesignStudio()
{
QSettings *settings = Core::ICore::settings();
const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode";
-
return settings->value(qdsStandaloneEntry, false).toBool();
}
@@ -659,172 +225,30 @@ bool QmlProject::isQtDesignStudioStartedFromQtC()
return qEnvironmentVariableIsSet(Constants::enviromentLaunchedQDS);
}
-ProjectExplorer::DeploymentKnowledge QmlProject::deploymentKnowledge() const
+DeploymentKnowledge QmlProject::deploymentKnowledge() const
{
return DeploymentKnowledge::Perfect;
}
-void QmlBuildSystem::generateProjectTree()
-{
- if (!m_projectItem)
- return;
-
- auto newRoot = std::make_unique<QmlProjectNode>(project());
-
- for (const QString &f : m_projectItem->files()) {
- const Utils::FilePath fileName = Utils::FilePath::fromString(f);
- const FileType fileType = (fileName == projectFilePath())
- ? FileType::Project : FileNode::fileTypeForFileName(fileName);
- newRoot->addNestedNode(std::make_unique<FileNode>(fileName, fileType));
- }
- newRoot->addNestedNode(std::make_unique<FileNode>(projectFilePath(), FileType::Project));
-
- setRootProjectNode(std::move(newRoot));
- refreshTargetDirectory();
-}
-
-void QmlBuildSystem::updateDeploymentData()
-{
- if (!m_projectItem)
- return;
-
- if (DeviceTypeKitAspect::deviceTypeId(kit())
- == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
- return;
- }
-
- ProjectExplorer::DeploymentData deploymentData;
- for (const QString &file : m_projectItem->files()) {
- deploymentData.addFile(
- FilePath::fromString(file),
- targetFile(Utils::FilePath::fromString(file)).parentDir().toString());
- }
-
- setDeploymentData(deploymentData);
-}
-
-QVariant QmlBuildSystem::additionalData(Id id) const
-{
- if (id == Constants::customFileSelectorsData)
- return customFileSelectors();
- if (id == Constants::supportedLanguagesData)
- return supportedLanguages();
- if (id == Constants::primaryLanguageData)
- return primaryLanguage();
- if (id == Constants::customForceFreeTypeData)
- return forceFreeType();
- if (id == Constants::customQtForMCUs)
- return qtForMCUs();
- if (id == Constants::customQt6Project)
- return qt6Project();
- if (id == Constants::mainFilePath)
- return mainFilePath().toString();
- if (id == Constants::customImportPaths)
- return customImportPaths();
- if (id == Constants::canonicalProjectDir)
- return canonicalProjectDir().toString();
- return {};
-}
-
-bool QmlBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
-{
- if (dynamic_cast<QmlProjectNode *>(context)) {
- if (action == AddNewFile || action == EraseFile)
- return true;
- QTC_ASSERT(node, return false);
-
- if (action == Rename && node->asFileNode()) {
- const FileNode *fileNode = node->asFileNode();
- QTC_ASSERT(fileNode, return false);
- return fileNode->fileType() != FileType::Project;
- }
-
- return false;
- }
-
- return BuildSystem::supportsAction(context, action, node);
-}
-
-QmlProject *QmlBuildSystem::qmlProject() const
-{
- return static_cast<QmlProject *>(BuildSystem::project());
-}
-
-bool QmlBuildSystem::forceFreeType() const
-{
- if (m_projectItem)
- return m_projectItem->forceFreeType();
- return false;
-}
-
-bool QmlBuildSystem::widgetApp() const
-{
- if (m_projectItem)
- return m_projectItem->widgetApp();
- return false;
-}
-
-QStringList QmlBuildSystem::shaderToolArgs() const
-{
- if (m_projectItem)
- return m_projectItem->shaderToolArgs();
- return {};
-}
-
-QStringList QmlBuildSystem::shaderToolFiles() const
-{
- if (m_projectItem)
- return m_projectItem->shaderToolFiles();
- return {};
-}
-
-QStringList QmlBuildSystem::importPaths() const
-{
- if (m_projectItem)
- return m_projectItem->importPaths();
- return {};
-}
-
-QStringList QmlBuildSystem::files() const
-{
- if (m_projectItem)
- return m_projectItem->files();
- return {};
-}
-
-bool QmlBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *)
+bool QmlProject::isEditModePreferred() const
{
- if (!dynamic_cast<QmlProjectNode *>(context))
- return false;
-
- FilePaths toAdd;
- for (const FilePath &filePath : filePaths) {
- if (!m_projectItem->matchesFile(filePath.toString()))
- toAdd << filePaths;
- }
- return toAdd.isEmpty();
+ return !isQtDesignStudio();
}
-bool QmlBuildSystem::deleteFiles(Node *context, const FilePaths &filePaths)
+int QmlProject::preferedQtTarget(Target *target)
{
- if (dynamic_cast<QmlProjectNode *>(context))
- return true;
+ if (!target)
+ return -1;
- return BuildSystem::deleteFiles(context, filePaths);
+ auto buildSystem = qobject_cast<QmlBuildSystem *>(target->buildSystem());
+ return (buildSystem && buildSystem->qt6Project()) ? 6 : 5;
}
-bool QmlBuildSystem::renameFile(Node * context, const FilePath &oldFilePath, const FilePath &newFilePath)
+bool QmlProject::allowOnlySingleProject()
{
- if (dynamic_cast<QmlProjectNode *>(context)) {
- if (oldFilePath.endsWith(mainFile().path()))
- return setMainFileInProjectFile(newFilePath);
- if (oldFilePath.endsWith(mainUiFile().path()))
- return setMainUiFileInProjectFile(newFilePath);
-
- return true;
- }
-
- return BuildSystem::renameFile(context, oldFilePath, newFilePath);
+ QSettings *settings = Core::ICore::settings();
+ auto key = "QML/Designer/AllowMultipleProjects";
+ return !settings->value(key, false).toBool();
}
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h
index f00a4f2b36c..3a6d5badf79 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.h
+++ b/src/plugins/qmlprojectmanager/qmlproject.h
@@ -1,150 +1,28 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
+#include "buildsystem/qmlbuildsystem.h" // IWYU pragma: keep
#include "qmlprojectmanager_global.h"
-
-#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
-#include <utils/environment.h>
-
-#include <QPointer>
-
namespace QmlProjectManager {
class QmlProject;
-class QmlProjectItem;
-
-class QMLPROJECTMANAGER_EXPORT QmlBuildSystem : public ProjectExplorer::BuildSystem
-{
- Q_OBJECT
-
-public:
- explicit QmlBuildSystem(ProjectExplorer::Target *target);
- ~QmlBuildSystem();
-
- void triggerParsing() final;
-
- bool supportsAction(ProjectExplorer::Node *context,
- ProjectExplorer::ProjectAction action,
- const ProjectExplorer::Node *node) const override;
- bool addFiles(ProjectExplorer::Node *context,
- const Utils::FilePaths &filePaths, Utils::FilePaths *notAdded = nullptr) override;
- bool deleteFiles(ProjectExplorer::Node *context,
- const Utils::FilePaths &filePaths) override;
- bool renameFile(ProjectExplorer::Node *context,
- const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath) override;
- QString name() const override { return QLatin1String("qml"); }
-
- QmlProject *qmlProject() const;
-
- QVariant additionalData(Utils::Id id) const override;
-
- enum RefreshOption {
- ProjectFile = 0x01,
- Files = 0x02,
- Configuration = 0x04,
- Everything = ProjectFile | Files | Configuration
- };
- Q_DECLARE_FLAGS(RefreshOptions,RefreshOption)
-
- void refresh(RefreshOptions options);
-
- Utils::FilePath canonicalProjectDir() const;
- Utils::FilePath mainFile() const;
- Utils::FilePath mainUiFile() const;
- Utils::FilePath mainFilePath() const;
- Utils::FilePath mainUiFilePath() const;
-
- bool setMainFileInProjectFile(const Utils::FilePath &newMainFilePath);
- bool setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath);
- bool setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath);
-
- bool qtForMCUs() const;
- bool qt6Project() const;
- void setMainFile(const QString &mainFilePath);
- Utils::FilePath targetDirectory() const;
- Utils::FilePath targetFile(const Utils::FilePath &sourceFile) const;
-
- Utils::EnvironmentItems environment() const;
- QStringList customImportPaths() const;
- QStringList customFileSelectors() const;
- bool multilanguageSupport() const;
- QStringList supportedLanguages() const;
- void setSupportedLanguages(QStringList languages);
- QString primaryLanguage() const;
- void setPrimaryLanguage(QString language);
- bool forceFreeType() const;
- bool widgetApp() const;
- QStringList shaderToolArgs() const;
- QStringList shaderToolFiles() const;
- QStringList importPaths() const;
- QStringList files() const;
-
- bool addFiles(const QStringList &filePaths);
-
- void refreshProjectFile();
-
- static Utils::FilePath activeMainFilePath();
- static QStringList makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths);
-
- void generateProjectTree();
- void updateDeploymentData();
- void refreshFiles(const QSet<QString> &added, const QSet<QString> &removed);
- void refreshTargetDirectory();
- void onActiveTargetChanged(ProjectExplorer::Target *target);
- void onKitChanged();
-
- // plain format
- void parseProject(RefreshOptions options);
-
-signals:
- void projectChanged();
-
-private:
- bool setFileSettingInProjectFile(const QString &setting,
- const Utils::FilePath &mainFilePath,
- const Utils::FilePath &oldFile);
-
- std::unique_ptr<QmlProjectItem> m_projectItem;
- Utils::FilePath m_canonicalProjectDir;
- bool m_blockFilesUpdate = false;
- friend class FilesUpdateBlocker;
-};
-
-class FilesUpdateBlocker {
-public:
- FilesUpdateBlocker(QmlBuildSystem* bs): m_bs(bs) {
- if (m_bs)
- m_bs->m_blockFilesUpdate = true;
- }
-
- ~FilesUpdateBlocker() {
- if (m_bs) {
- m_bs->m_blockFilesUpdate = false;
- m_bs->refresh(QmlBuildSystem::Everything);
- }
- }
-private:
- QPointer<QmlBuildSystem> m_bs;
-};
class QMLPROJECTMANAGER_EXPORT QmlProject : public ProjectExplorer::Project
{
Q_OBJECT
-
public:
explicit QmlProject(const Utils::FilePath &filename);
- ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final;
-
static bool isQtDesignStudio();
static bool isQtDesignStudioStartedFromQtC();
-
bool isEditModePreferred() const override;
+ ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final;
+
protected:
RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override;
@@ -153,9 +31,35 @@ private:
Utils::FilePaths collectUiQmlFilesForFolder(const Utils::FilePath &folder) const;
Utils::FilePaths collectQmlFiles() const;
- QMetaObject::Connection m_openFileConnection;
+ bool setKitWithVersion(const int qtMajorVersion, const QList<ProjectExplorer::Kit *> kits);
+
+ bool allowOnlySingleProject();
+ int preferedQtTarget(ProjectExplorer::Target *target);
+
+private slots:
+ void parsingFinished(const ProjectExplorer::Target *target, bool success);
};
-} // namespace QmlProjectManager
+class FilesUpdateBlocker
+{
+public:
+ FilesUpdateBlocker(QmlBuildSystem *bs)
+ : m_bs(bs)
+ {
+ if (m_bs)
+ m_bs->m_blockFilesUpdate = true;
+ }
+
+ ~FilesUpdateBlocker()
+ {
+ if (m_bs) {
+ m_bs->m_blockFilesUpdate = false;
+ m_bs->refresh(QmlBuildSystem::RefreshOptions::Project);
+ }
+ }
+
+private:
+ QPointer<QmlBuildSystem> m_bs;
+};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QmlProjectManager::QmlBuildSystem::RefreshOptions)
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
index d4938c8d798..e4e4d0bc247 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
+++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
@@ -34,12 +34,14 @@ QtcPlugin {
}
Group {
- name: "File Format"
- prefix: "fileformat/"
+ name: "Build System"
+ prefix: "buildsystem/"
files: [
- "filefilteritems.cpp", "filefilteritems.h",
- "qmlprojectfileformat.cpp", "qmlprojectfileformat.h",
- "qmlprojectitem.cpp", "qmlprojectitem.h",
+ "qmlbuildsystem.cpp", "qmlbuildsystem.h",
+ "projectitem/filefilteritems.cpp", "projectitem/filefilteritems.h",
+ "projectitem/qmlprojectitem.cpp", "projectitem/qmlprojectitem.h",
+ "projectitem/converters.h",
+ "projectnode/qmlprojectnodes.cpp", "projectnode/qmlprojectnodes.h"
]
}
@@ -56,7 +58,7 @@ QtcPlugin {
"cmakeprojectconverterdialog.cpp", "cmakeprojectconverterdialog.h",
]
}
-
+
Group {
name: "QML Project File Generator"
prefix: "qmlprojectgen/"
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
index 2ead9de6da9..2876c3c366a 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
@@ -10,7 +10,6 @@
#include "projectfilecontenttools.h"
#include "cmakegen/cmakeprojectconverter.h"
#include "cmakegen/generatecmakelists.h"
-#include "qmlprojectgen/qmlprojectgenerator.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>