aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTapani Mattila <tapani.mattila@qt.io>2021-10-06 12:35:52 +0300
committerTapani Mattila <tapani.mattila@qt.io>2021-10-11 12:35:19 +0000
commitd1b62588c50aa6aaeb9356b9baa3255c40a65a5b (patch)
tree0aa2900b6ee467141e28a08f7e6eb38f3aac5167
parent6669a9e44223ac776beaabd8da8c985cba7d5017 (diff)
Add action to generate CMake files for QML projects
Task-number: QDS-5140 Change-Id: I56d2d3d9efce4bea1281eb60a059119f5b29c02c Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt1
-rw-r--r--src/plugins/qmldesigner/generatecmakelists.cpp271
-rw-r--r--src/plugins/qmldesigner/generatecmakelists.h45
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp3
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.pri2
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.qbs2
6 files changed, 324 insertions, 0 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index ce73af5161..cee0c2e369 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -27,6 +27,7 @@ add_qtc_plugin(QmlDesigner
documentmanager.cpp documentmanager.h
documentwarningwidget.cpp documentwarningwidget.h
generateresource.cpp generateresource.h
+ generatecmakelists.cpp generatecmakelists.h
openuiqmlfiledialog.cpp openuiqmlfiledialog.h openuiqmlfiledialog.ui
qmldesignerconstants.h
qmldesignericons.h
diff --git a/src/plugins/qmldesigner/generatecmakelists.cpp b/src/plugins/qmldesigner/generatecmakelists.cpp
new file mode 100644
index 0000000000..d7dc13fd5b
--- /dev/null
+++ b/src/plugins/qmldesigner/generatecmakelists.cpp
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "generatecmakelists.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/session.h>
+
+#include <qmlprojectmanager/qmlprojectmanagerconstants.h>
+
+#include <utils/fileutils.h>
+
+#include <QAction>
+#include <QRegularExpression>
+#include <QStringList>
+
+using namespace Utils;
+
+namespace QmlDesigner {
+namespace GenerateCmakeLists {
+
+const QDir::Filters FILES_ONLY = QDir::Files;
+const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot;
+
+const char CMAKEFILENAME[] = "CMakeLists.txt";
+const char QMLDIRFILENAME[] = "qmldir";
+
+void generateMenuEntry()
+{
+ Core::ActionContainer *buildMenu =
+ Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
+ const Core::Context projectCntext(QmlProjectManager::Constants::QML_PROJECT_ID);
+ auto action = new QAction("Generate CMakeLists.txt files");
+ QObject::connect(action, &QAction::triggered, GenerateCmakeLists::onGenerateCmakeLists);
+ Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists");
+ buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN);
+
+ action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ QObject::connect(ProjectExplorer::SessionManager::instance(),
+ &ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
+ action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ });
+}
+
+void onGenerateCmakeLists()
+{
+ generateMainCmake(ProjectExplorer::SessionManager::startupProject()->projectDirectory());
+}
+
+QStringList processDirectory(const FilePath &dir)
+{
+ QStringList moduleNames;
+
+ FilePaths files = dir.dirEntries(FILES_ONLY);
+ for (FilePath &file : files) {
+ if (!file.fileName().compare(CMAKEFILENAME))
+ files.removeAll(file);
+ }
+
+ if (files.isEmpty()) {
+ generateSubdirCmake(dir);
+ FilePaths subDirs = dir.dirEntries(DIRS_ONLY);
+ for (FilePath &subDir : subDirs) {
+ QStringList subDirModules = processDirectory(subDir);
+ moduleNames.append(subDirModules);
+ }
+ }
+ else {
+ QString moduleName = generateModuleCmake(dir);
+ if (!moduleName.isEmpty()) {
+ moduleNames.append(moduleName);
+ }
+ }
+
+ return moduleNames;
+}
+
+const char MAINFILE_REQUIRED_VERSION[] = "cmake_minimum_required(VERSION 3.18)\n\n";
+const char MAINFILE_PROJECT[] = "project(%1 LANGUAGES CXX)\n\n";
+const char MAINFILE_CMAKE_OPTIONS[] = "set(CMAKE_INCLUDE_CURRENT_DIR ON)\nset(CMAKE_AUTOMOC ON)\n\n";
+const char MAINFILE_PACKAGES[] = "find_package(Qt6 COMPONENTS Gui Qml Quick)\n";
+const char MAINFILE_LIBRARIES[] = "set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)\n\n";
+const char MAINFILE_CPP[] = "add_executable(%1 main.cpp)\n\n";
+const char MAINFILE_MAINMODULE[] = "qt6_add_qml_module(%1\n\tURI \"Main\"\n\tVERSION 1.0\n\tNO_PLUGIN\n\tQML_FILES main.qml\n)\n\n";
+const char MAINFILE_LINK_LIBRARIES[] = "target_link_libraries(%1 PRIVATE\n\tQt${QT_VERSION_MAJOR}::Core\n\tQt${QT_VERSION_MAJOR}::Gui\n\tQt${QT_VERSION_MAJOR}::Quick\n\tQt${QT_VERSION_MAJOR}::Qml\n)\n\n";
+
+const char ADD_SUBDIR[] = "add_subdirectory(%1)\n";
+
+void generateMainCmake(const FilePath &rootDir)
+{
+ //TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all.
+ QString projectName = ProjectExplorer::SessionManager::startupProject()->displayName();
+
+ FilePaths subDirs = rootDir.dirEntries(DIRS_ONLY);
+
+ QString fileContent;
+ fileContent.append(MAINFILE_REQUIRED_VERSION);
+ fileContent.append(QString(MAINFILE_PROJECT).arg(projectName));
+ fileContent.append(MAINFILE_CMAKE_OPTIONS);
+ fileContent.append(MAINFILE_PACKAGES);
+ fileContent.append(QString(MAINFILE_CPP).arg(projectName));
+ fileContent.append(QString(MAINFILE_MAINMODULE).arg(projectName));
+ fileContent.append(MAINFILE_LIBRARIES);
+
+ for (FilePath &subDir : subDirs) {
+ QStringList subDirModules = processDirectory(subDir);
+ if (!subDirModules.isEmpty())
+ fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName()));
+ }
+ fileContent.append("\n");
+
+ fileContent.append(QString(MAINFILE_LINK_LIBRARIES).arg(projectName));
+
+ createCmakeFile(rootDir, fileContent);
+}
+
+const char MODULEFILE_PROPERTY_SINGLETON[] = "QT_QML_SINGLETON_TYPE";
+const char MODULEFILE_PROPERTY_SET[] = "set_source_files_properties(%1\n\tPROPERTIES\n\t\t%2 %3\n)\n\n";
+const char MODULEFILE_CREATE_MODULE[] = "qt6_add_qml_module(%1\n\tURI \"%1\"\n\tVERSION 1.0\n%2)\n\n";
+
+
+QString generateModuleCmake(const FilePath &dir)
+{
+ QString fileContent;
+ const QStringList qmlFilesOnly("*.qml");
+ const QStringList qmldirFilesOnly(QMLDIRFILENAME);
+ ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+
+ FilePaths qmldirFileList = dir.dirEntries(qmldirFilesOnly, FILES_ONLY);
+ if (!qmldirFileList.isEmpty()) {
+ QStringList singletons = getSingletonsFromQmldirFile(qmldirFileList.first());
+ for (QString &singleton : singletons) {
+ fileContent.append(QString(MODULEFILE_PROPERTY_SET).arg(singleton).arg(MODULEFILE_PROPERTY_SINGLETON).arg("true"));
+ }
+ }
+
+ FilePaths qmlFileList = dir.dirEntries(qmlFilesOnly, FILES_ONLY);
+ QString qmlFiles;
+ for (FilePath &qmlFile : qmlFileList) {
+ if (project->isKnownFile(qmlFile))
+ qmlFiles.append(QString("\t\t%1\n").arg(qmlFile.fileName()));
+ }
+
+ QStringList resourceFileList = getDirectoryTreeResources(dir);
+ QString resourceFiles;
+ for (QString &resourceFile : resourceFileList) {
+ resourceFiles.append(QString("\t\t%1\n").arg(resourceFile));
+ }
+
+ QString moduleContent;
+ if (!qmlFiles.isEmpty()) {
+ moduleContent.append(QString("\tQML_FILES\n%1").arg(qmlFiles));
+ }
+ if (!resourceFiles.isEmpty()) {
+ moduleContent.append(QString("\tRESOURCES\n%1").arg(resourceFiles));
+ }
+
+ QString moduleName = dir.fileName();
+
+ fileContent.append(QString(MODULEFILE_CREATE_MODULE).arg(moduleName).arg(moduleContent));
+
+ createCmakeFile(dir, fileContent);
+
+ return moduleName;
+}
+
+void generateSubdirCmake(const FilePath &dir)
+{
+ QString fileContent;
+ FilePaths subDirs = dir.dirEntries(DIRS_ONLY);
+
+ for (FilePath &subDir : subDirs) {
+ fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName()));
+ }
+
+ createCmakeFile(dir, fileContent);
+}
+
+QStringList getSingletonsFromQmldirFile(const FilePath &filePath)
+{
+ QStringList singletons;
+ QFile f(filePath.toString());
+ f.open(QIODevice::ReadOnly);
+ QTextStream stream(&f);
+
+ while (!stream.atEnd()) {
+ QString line = stream.readLine();
+ if (line.startsWith("singleton", Qt::CaseInsensitive)) {
+ QStringList tokenizedLine = line.split(QRegularExpression("\\s+"));
+ QString fileName = tokenizedLine.last();
+ if (fileName.endsWith(".qml", Qt::CaseInsensitive)) {
+ singletons.append(fileName);
+ }
+ }
+ }
+
+ f.close();
+
+ return singletons;
+}
+
+QStringList getDirectoryTreeResources(const FilePath &dir)
+{
+ ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ QStringList resourceFileList;
+
+ FilePaths thisDirFiles = dir.dirEntries(FILES_ONLY);
+ for (FilePath &file : thisDirFiles) {
+ if (!isFileBlacklisted(file.fileName()) &&
+ !file.fileName().endsWith(".qml", Qt::CaseInsensitive) &&
+ project->isKnownFile(file)) {
+ resourceFileList.append(file.fileName());
+ }
+ }
+
+ FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
+ for (FilePath &subDir : subDirsList) {
+ QStringList subDirResources = getDirectoryTreeResources(subDir);
+ for (QString &resource : subDirResources) {
+ resourceFileList.append(subDir.fileName().append('/').append(resource));
+ }
+
+ }
+
+ return resourceFileList;
+}
+
+void createCmakeFile(const FilePath &dir, const QString &content)
+{
+ FilePath filePath = dir.pathAppended(CMAKEFILENAME);
+ QFile cmakeFile(filePath.toString());
+ cmakeFile.open(QIODevice::WriteOnly);
+ QTextStream stream(&cmakeFile);
+ stream << content;
+ cmakeFile.close();
+}
+
+bool isFileBlacklisted(const QString &fileName)
+{
+ return (!fileName.compare(QMLDIRFILENAME) ||
+ !fileName.compare(CMAKEFILENAME));
+}
+
+}
+}
diff --git a/src/plugins/qmldesigner/generatecmakelists.h b/src/plugins/qmldesigner/generatecmakelists.h
new file mode 100644
index 0000000000..55b0f6958d
--- /dev/null
+++ b/src/plugins/qmldesigner/generatecmakelists.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <projectexplorer/project.h>
+
+#include <utils/fileutils.h>
+
+namespace QmlDesigner {
+namespace GenerateCmakeLists {
+void generateMenuEntry();
+void onGenerateCmakeLists();
+void generateMainCmake(const Utils::FilePath &rootDir);
+void generateSubdirCmake(const Utils::FilePath &dir);
+QString generateModuleCmake(const Utils::FilePath &dir);
+QStringList processDirectory(const Utils::FilePath &dir);
+QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath);
+QStringList getDirectoryTreeResources(const Utils::FilePath &dir);
+void createCmakeFile(const Utils::FilePath &filePath, const QString &content);
+bool isFileBlacklisted(const QString &fileName);
+}
+}
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 77c4715f17..014c79b59d 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -31,6 +31,7 @@
#include "designmodecontext.h"
#include "openuiqmlfiledialog.h"
#include "generateresource.h"
+#include "generatecmakelists.h"
#include "nodeinstanceview.h"
#include "gestures.h"
@@ -222,6 +223,8 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool())
GenerateResource::generateMenuEntry();
+ GenerateCmakeLists::generateMenuEntry();
+
const QString fontPath
= Core::ICore::resourcePath(
"qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf")
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pri b/src/plugins/qmldesigner/qmldesignerplugin.pri
index 58fb55788f..4cf7edf4ba 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.pri
+++ b/src/plugins/qmldesigner/qmldesignerplugin.pri
@@ -5,6 +5,7 @@ HEADERS += $$PWD/qmldesignerconstants.h \
$$PWD/designersettings.h \
$$PWD/editorproxy.h \
$$PWD/generateresource.h \
+ $$PWD/generatecmakelists.h \
$$PWD/settingspage.h \
$$PWD/designmodecontext.h \
$$PWD/documentmanager.h \
@@ -20,6 +21,7 @@ SOURCES += $$PWD/qmldesignerplugin.cpp \
$$PWD/designersettings.cpp \
$$PWD/editorproxy.cpp \
$$PWD/generateresource.cpp \
+ $$PWD/generatecmakelists.cpp \
$$PWD/settingspage.cpp \
$$PWD/designmodecontext.cpp \
$$PWD/documentmanager.cpp \
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs
index a1cf95c0e8..a3b70ec970 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.qbs
+++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs
@@ -1007,6 +1007,8 @@ Project {
files: [
"generateresource.cpp",
"generateresource.h",
+ "generatecmakelists.cpp",
+ "generatecmakelists.h",
"designersettings.cpp",
"designersettings.h",
"designmodecontext.cpp",