diff options
author | Tapani Mattila <tapani.mattila@qt.io> | 2021-10-06 12:35:52 +0300 |
---|---|---|
committer | Tapani Mattila <tapani.mattila@qt.io> | 2021-10-11 12:35:19 +0000 |
commit | d1b62588c50aa6aaeb9356b9baa3255c40a65a5b (patch) | |
tree | 0aa2900b6ee467141e28a08f7e6eb38f3aac5167 | |
parent | 6669a9e44223ac776beaabd8da8c985cba7d5017 (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.txt | 1 | ||||
-rw-r--r-- | src/plugins/qmldesigner/generatecmakelists.cpp | 271 | ||||
-rw-r--r-- | src/plugins/qmldesigner/generatecmakelists.h | 45 | ||||
-rw-r--r-- | src/plugins/qmldesigner/qmldesignerplugin.cpp | 3 | ||||
-rw-r--r-- | src/plugins/qmldesigner/qmldesignerplugin.pri | 2 | ||||
-rw-r--r-- | src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 |
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", |