diff options
author | Tim Jenssen <[email protected]> | 2023-05-22 19:48:42 +0200 |
---|---|---|
committer | Tim Jenssen <[email protected]> | 2023-05-22 21:13:40 +0200 |
commit | 1b6c0ff56c8d48c5b5bb8044b113cd580f6d5ee3 (patch) | |
tree | 25878f9091506fd282a338a2c5f98b357d47134e /src/plugins/qmlprojectmanager | |
parent | 3dcdbe9069c452e2f0eacb925aa7412e63dc4762 (diff) | |
parent | df7398e2c5f3c1595f32c7484ac1e804d83a01ca (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')
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> |