diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index b501a98bdd1..742b6f055c0 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -30,6 +30,8 @@ add_qtc_plugin(QmlDesigner
checkablefiletreeitem.cpp checkablefiletreeitem.h
cmakegeneratordialog.cpp cmakegeneratordialog.h
cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h
+ cmakeprojectconverter.cpp cmakeprojectconverter.h
+ cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h
generateresource.cpp generateresource.h
generatecmakelists.cpp generatecmakelists.h
generatecmakelistsconstants.h
diff --git a/src/plugins/qmldesigner/boilerplate.qrc b/src/plugins/qmldesigner/boilerplate.qrc
index 790f605c08e..a89643d6fff 100644
--- a/src/plugins/qmldesigner/boilerplate.qrc
+++ b/src/plugins/qmldesigner/boilerplate.qrc
@@ -7,5 +7,6 @@
qmlprojectmaincmakelists.tpl
qmlprojectmodulecmakelists.tpl
qmlprojectmainqml.tpl
+ qmlprojectappmainqml.tpl
diff --git a/src/plugins/qmldesigner/cmakegeneratordialog.cpp b/src/plugins/qmldesigner/cmakegeneratordialog.cpp
index eb7a35f170c..e8cf8e872f4 100644
--- a/src/plugins/qmldesigner/cmakegeneratordialog.cpp
+++ b/src/plugins/qmldesigner/cmakegeneratordialog.cpp
@@ -151,5 +151,5 @@ void CmakeGeneratorDialog::refreshNotificationText()
}
}
-}
-}
+} //GenerateCmake
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/cmakeprojectconverter.cpp b/src/plugins/qmldesigner/cmakeprojectconverter.cpp
new file mode 100644
index 00000000000..52448835771
--- /dev/null
+++ b/src/plugins/qmldesigner/cmakeprojectconverter.cpp
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 "cmakeprojectconverter.h"
+#include "cmakeprojectconverterdialog.h"
+#include "generatecmakelists.h"
+#include "generatecmakelistsconstants.h"
+
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+using namespace Utils;
+using namespace QmlDesigner::GenerateCmake::Constants;
+
+namespace QmlDesigner {
+namespace GenerateCmake {
+
+const QString MENU_ITEM_CONVERT = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter",
+ "Export as Latest Project Format");
+const QString ERROR_TITLE = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter",
+ "Creating Project");
+const QString SUCCESS_TITLE = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter",
+ "Creating Project");
+const QString ERROR_TEXT = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter",
+ "Creating project failed.\n%1");
+const QString SUCCESS_TEXT = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter",
+ "Creating project succeeded.");
+
+void CmakeProjectConverter::generateMenuEntry()
+{
+ Core::ActionContainer *menu =
+ Core::ActionManager::actionContainer(Core::Constants::M_FILE);
+ auto action = new QAction(MENU_ITEM_CONVERT);
+ QObject::connect(action, &QAction::triggered, CmakeProjectConverter::onConvertProject);
+ Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ConvertToCmakeProject");
+ menu->addAction(cmd, Core::Constants::G_FILE_EXPORT);
+
+ action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ QObject::connect(ProjectExplorer::SessionManager::instance(),
+ &ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
+ action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ });
+}
+
+void CmakeProjectConverter::onConvertProject()
+{
+ ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ const QmlProjectManager::QmlProject *qmlProject =
+ qobject_cast(project);
+ if (qmlProject) {
+ CmakeProjectConverterDialog dialog(qmlProject);
+ if (dialog.exec()) {
+ FilePath newProjectPath = dialog.newPath();
+ CmakeProjectConverter converter;
+ converter.convertProject(qmlProject, newProjectPath);
+ }
+ }
+}
+
+bool CmakeProjectConverter::convertProject(const QmlProjectManager::QmlProject *project,
+ const FilePath &targetDir)
+{
+ m_converterObjects.clear();
+ m_projectDir = project->projectDirectory();
+ m_newProjectDir = targetDir;
+ m_project = project;
+
+ m_rootDirFiles = QStringList(FILENAME_FILTER_QMLPROJECT);
+ const QString confFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
+ if (!confFile.isEmpty())
+ m_rootDirFiles.append(confFile);
+
+ bool retVal = prepareAndExecute();
+
+ if (retVal)
+ QMessageBox::information(nullptr, SUCCESS_TITLE, SUCCESS_TEXT);
+ else
+ QMessageBox::critical(nullptr, ERROR_TITLE, ERROR_TEXT.arg(m_errorText));
+
+ return retVal;
+}
+
+bool CmakeProjectConverter::prepareAndExecute()
+{
+ GenerateCmake::CmakeFileGenerator cmakeGenerator;
+
+ if (!performSanityCheck())
+ return false;
+ if (!prepareBaseDirectoryStructure())
+ return false;
+ if (!prepareCopy())
+ return false;
+ if (!createPreparedProject())
+ return false;
+ if (!cmakeGenerator.prepare(m_newProjectDir, false))
+ return false;
+ if (!cmakeGenerator.execute())
+ return false;
+ if (!modifyNewFiles())
+ return false;
+
+ return true;
+}
+
+bool CmakeProjectConverter::isFileBlacklisted(const Utils::FilePath &file) const
+{
+ if (!file.fileName().compare(FILENAME_CMAKELISTS))
+ return true;
+ if (!file.suffix().compare(FILENAME_SUFFIX_QMLPROJECT))
+ return true;
+ if (!file.suffix().compare(FILENAME_SUFFIX_USER))
+ return true;
+ if (m_rootDirFiles.contains(file.fileName()))
+ return true;
+
+ return false;
+}
+
+bool CmakeProjectConverter::isDirBlacklisted(const Utils::FilePath &dir) const
+{
+ if (!dir.isDir())
+ return true;
+
+ return false;
+}
+
+const QString ERROR_CANNOT_WRITE_DIR = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter",
+ "Unable to write to directory\n%1.");
+
+bool CmakeProjectConverter::performSanityCheck()
+{
+ if (!m_newProjectDir.parentDir().isWritableDir()) {
+ m_errorText = ERROR_CANNOT_WRITE_DIR.arg(m_newProjectDir.parentDir().toString());
+ return false;
+ }
+
+ return true;
+}
+
+bool CmakeProjectConverter::prepareBaseDirectoryStructure()
+{
+ addDirectory(m_newProjectDir);
+ addDirectory(contentDir());
+ addDirectory(sourceDir());
+ addDirectory(importDir());
+ addDirectory(assetDir());
+ addDirectory(assetImportDir());
+ addFile(contentDir().pathAppended(FILENAME_APPMAINQML));
+
+ return true;
+}
+
+bool CmakeProjectConverter::prepareCopy()
+{
+ FilePaths rootFiles = m_projectDir.dirEntries({m_rootDirFiles, QDir::Files});
+ for (const FilePath &file : rootFiles) {
+ addFile(file, m_newProjectDir.pathAppended(file.fileName()));
+ }
+
+ prepareCopyDirFiles(m_projectDir, contentDir());
+
+ FilePaths subDirs = m_projectDir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot);
+ for (FilePath &subDir : subDirs) {
+ if (subDir.fileName() == DIRNAME_IMPORT) {
+ prepareCopyDirTree(subDir, importDir());
+ }
+ else if (subDir.fileName() == DIRNAME_CPP) {
+ prepareCopyDirTree(subDir, sourceDir());
+ }
+ else if (subDir.fileName() == DIRNAME_ASSET) {
+ prepareCopyDirTree(subDir, assetDir());
+ }
+ else if (subDir.fileName() == DIRNAME_ASSETIMPORT) {
+ prepareCopyDirTree(subDir, assetImportDir());
+ }
+ else {
+ prepareCopyDirTree(subDir, contentDir().pathAppended(subDir.fileName()));
+ }
+ }
+
+ return true;
+}
+
+bool CmakeProjectConverter::prepareCopyDirFiles(const FilePath &dir, const FilePath &targetDir)
+{
+ FilePaths dirFiles = dir.dirEntries(QDir::Files);
+ for (FilePath file : dirFiles) {
+ if (!isFileBlacklisted(file))
+ addFile(file, targetDir.pathAppended(file.fileName()));
+ }
+
+ return true;
+}
+
+bool CmakeProjectConverter::prepareCopyDirTree(const FilePath &dir, const FilePath &targetDir)
+{
+ prepareCopyDirFiles(dir, targetDir);
+ FilePaths subDirs = dir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot);
+ for (FilePath &subDir : subDirs) {
+ if (isDirBlacklisted(subDir))
+ continue;
+ addDirectory(targetDir.pathAppended(subDir.fileName()));
+ prepareCopyDirFiles(subDir, targetDir.pathAppended(subDir.fileName()));
+ prepareCopyDirTree(subDir, targetDir.pathAppended(subDir.fileName()));
+ }
+
+ return true;
+}
+
+bool CmakeProjectConverter::addDirectory(const Utils::FilePath &target)
+{
+ return addObject(ProjectConverterObjectType::Directory, FilePath(), target);
+}
+
+bool CmakeProjectConverter::addFile(const Utils::FilePath &target)
+{
+ return addFile(FilePath(), target);
+}
+
+bool CmakeProjectConverter::addFile(const Utils::FilePath &original, const Utils::FilePath &target)
+{
+ addDirectory(target.parentDir());
+ return addObject(ProjectConverterObjectType::File, original, target);
+}
+
+bool CmakeProjectConverter::addObject(ProjectConverterObjectType type,
+ const Utils::FilePath &original, const Utils::FilePath &target)
+{
+ if (target.isChildOf(m_projectDir))
+ return false;
+
+ if (!target.isChildOf(m_newProjectDir) &&
+ ((type == ProjectConverterObjectType::Directory) && (target != m_newProjectDir))) {
+ return false;
+ }
+
+ for (ProjectConverterObject &o : m_converterObjects) {
+ if (o.target == target)
+ return false;
+ }
+
+ ProjectConverterObject object;
+ object.type = type;
+ object.target = target;
+ object.original = original;
+
+ m_converterObjects.append(object);
+
+ return true;
+}
+
+bool CmakeProjectConverter::createPreparedProject()
+{
+ for (ProjectConverterObject &pco : m_converterObjects) {
+ if (pco.type == ProjectConverterObjectType::Directory) {
+ pco.target.createDir();
+ }
+ else if (pco.type == ProjectConverterObjectType::File) {
+ if (pco.original.isEmpty()) {
+ QFile newFile(pco.target.toString());
+ newFile.open(QIODevice::WriteOnly);
+ newFile.close();
+ }
+ else {
+ pco.original.copyFile(pco.target);
+ }
+ }
+ }
+
+ return true;
+}
+
+const FilePath CmakeProjectConverter::contentDir()
+{
+ return m_newProjectDir.pathAppended(DIRNAME_CONTENT);
+}
+
+const FilePath CmakeProjectConverter::sourceDir()
+{
+ return m_newProjectDir.pathAppended(DIRNAME_CPP);
+}
+
+const FilePath CmakeProjectConverter::importDir()
+{
+ return m_newProjectDir.pathAppended(DIRNAME_IMPORT);
+}
+
+const FilePath CmakeProjectConverter::assetDir()
+{
+ return contentDir().pathAppended(DIRNAME_ASSET);
+}
+
+const FilePath CmakeProjectConverter::assetImportDir()
+{
+ return m_newProjectDir.pathAppended(DIRNAME_ASSETIMPORT);
+}
+
+const FilePath CmakeProjectConverter::projectMainFile() const
+{
+ auto *target = m_project->activeTarget();
+ if (target && target->buildSystem()) {
+ auto buildSystem = qobject_cast(target->buildSystem());
+ if (buildSystem) {
+ return buildSystem->mainFilePath();
+ }
+ }
+ return {};
+}
+
+const QString CmakeProjectConverter::projectMainClass() const
+{
+ return projectMainFile().baseName();
+}
+
+bool CmakeProjectConverter::modifyNewFiles()
+{
+ return modifyAppMainQml() && modifyProjectFile();
+}
+
+const char APPMAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectappmainqml.tpl";
+
+bool CmakeProjectConverter::modifyAppMainQml()
+{
+ QString appMainQmlPath = contentDir().pathAppended(FILENAME_APPMAINQML).toString();
+ QFile appMainQml(appMainQmlPath);
+ appMainQml.open(QIODevice::ReadWrite);
+ if (!appMainQml.isOpen())
+ return false;
+
+ QString templateContent = GenerateCmake::readTemplate(APPMAIN_QMLFILE_TEMPLATE_PATH);
+ QString appMainQmlContent = templateContent.arg(projectMainClass());
+
+ appMainQml.reset();
+ appMainQml.write(appMainQmlContent.toUtf8());
+ appMainQml.close();
+
+ return true;
+}
+
+bool CmakeProjectConverter::modifyProjectFile()
+{
+ QString projectFileName = m_project->projectFilePath().fileName();
+ FilePath projectFilePath = m_newProjectDir.pathAppended(projectFileName);
+ QFile projectFile(projectFilePath.toString());
+ projectFile.open(QIODevice::ReadWrite);
+ if (!projectFile.isOpen())
+ return false;
+
+ QString projectFileContent = QString::fromUtf8(projectFile.readAll());
+ const QRegularExpression mainFilePattern("^\\s*mainFile:\\s*\".*\"", QRegularExpression::MultilineOption);
+ const QString mainFileString(" mainFile: \"content/App.qml\"");
+
+ projectFileContent.replace(mainFilePattern, mainFileString);
+
+ projectFile.reset();
+ projectFile.write(projectFileContent.toUtf8());
+ projectFile.close();
+
+ return true;
+}
+
+} //GenerateCmake
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/cmakeprojectconverter.h b/src/plugins/qmldesigner/cmakeprojectconverter.h
new file mode 100644
index 00000000000..4b21d65ac8b
--- /dev/null
+++ b/src/plugins/qmldesigner/cmakeprojectconverter.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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.
+**
+****************************************************************************/
+
+#ifndef CMAKEPROJECTCONVERTER_H
+#define CMAKEPROJECTCONVERTER_H
+
+#include
+#include
+
+namespace QmlDesigner {
+
+namespace GenerateCmake {
+
+enum ProjectConverterObjectType {
+ File,
+ Directory
+};
+
+struct ProjectConverterObject {
+ ProjectConverterObjectType type;
+ Utils::FilePath target;
+ Utils::FilePath original;
+};
+
+class CmakeProjectConverter {
+
+public:
+ bool convertProject(const QmlProjectManager::QmlProject *project,
+ const Utils::FilePath &targetDir);
+ static void generateMenuEntry();
+ static void onConvertProject();
+
+private:
+ bool prepareAndExecute();
+ bool isFileBlacklisted(const Utils::FilePath &file) const;
+ bool isDirBlacklisted(const Utils::FilePath &dir) const;
+ bool performSanityCheck();
+ bool prepareBaseDirectoryStructure();
+ bool prepareCopyDirFiles(const Utils::FilePath &dir, const Utils::FilePath &targetDir);
+ bool prepareCopyDirTree(const Utils::FilePath &dir, const Utils::FilePath &targetDir);
+ bool prepareCopy();
+ bool addDirectory(const Utils::FilePath &target);
+ bool addFile(const Utils::FilePath &target);
+ bool addFile(const Utils::FilePath &original, const Utils::FilePath &target);
+ bool addObject(ProjectConverterObjectType type,
+ const Utils::FilePath &original, const Utils::FilePath &target);
+ bool createPreparedProject();
+
+ const Utils::FilePath contentDir();
+ const Utils::FilePath sourceDir();
+ const Utils::FilePath importDir();
+ const Utils::FilePath assetDir();
+ const Utils::FilePath assetImportDir();
+
+ const QString environmentVariable(const QString &key) const;
+ const Utils::FilePath projectMainFile() const;
+ const QString projectMainClass() const;
+ bool modifyNewFiles();
+ bool modifyAppMainQml();
+ bool modifyProjectFile();
+
+private:
+ QList m_converterObjects;
+ QStringList m_rootDirFiles;
+ Utils::FilePath m_projectDir;
+ Utils::FilePath m_newProjectDir;
+ const QmlProjectManager::QmlProject *m_project;
+ QString m_errorText;
+};
+
+} //GenerateCmake
+} //QmlDesigner
+
+#endif // CMAKEPROJECTCONVERTER_H
diff --git a/src/plugins/qmldesigner/cmakeprojectconverterdialog.cpp b/src/plugins/qmldesigner/cmakeprojectconverterdialog.cpp
new file mode 100644
index 00000000000..1c15043779c
--- /dev/null
+++ b/src/plugins/qmldesigner/cmakeprojectconverterdialog.cpp
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 "cmakeprojectconverterdialog.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace Utils;
+
+namespace QmlDesigner {
+namespace GenerateCmake {
+
+const QRegularExpression projectNameRegexp("^(?!(import))(?!(QtQml))(?!(QtQuick))(?:[A-Z][a-zA-Z0-9-_]*)$");
+
+static bool projectNameValidationFunction(FancyLineEdit *editor, QString *)
+{
+ return editor->text().count(projectNameRegexp);
+}
+
+static bool dirValidationFunction(FancyLineEdit *editor, QString *)
+{
+ return FilePath::fromString(editor->text()).isWritableDir();
+}
+
+const QString EXPLANATION_TEXT = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "This process creates a copy of the existing project. The new project's folder structure is adjusted for CMake build process and necessary related new files are generated.\n\nThe new project can be opened in Qt Creator using the main CMakeLists.txt file.");
+
+const QString PROJECT_NAME_LABEL = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Name:");
+
+const QString PARENT_DIR_LABEL = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Create in:");
+
+CmakeProjectConverterDialog::CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject)
+ : QDialog()
+{
+ const FilePath defaultDir = Core::DocumentManager::projectsDirectory();
+ const QString defaultName = uniqueProjectName(defaultDir, oldProject->displayName());
+
+ QLabel *mainLabel = new QLabel(EXPLANATION_TEXT, this);
+ mainLabel->setWordWrap(true);
+
+ mainLabel->setMargin(20);
+ mainLabel->setMinimumWidth(600);
+
+ m_errorLabel = new InfoLabel();
+ m_errorLabel->setType(InfoLabel::InfoType::None);
+
+ m_nameEditor = new FancyLineEdit();
+ m_nameEditor->setValidationFunction(projectNameValidationFunction);
+ m_nameEditor->setText(defaultName);
+
+ m_dirSelector = new PathChooser();
+ m_dirSelector->setExpectedKind(PathChooser::Directory);
+ m_dirSelector->setValidationFunction(dirValidationFunction);
+ m_dirSelector->setPath(defaultDir.toString());
+
+ QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
+ m_okButton = buttons->button(QDialogButtonBox::Ok);
+ m_okButton->setDefault(true);
+
+ connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ connect(m_nameEditor, &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged);
+ connect(m_dirSelector->lineEdit(), &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged);
+
+ QGroupBox *form = new QGroupBox();
+ QFormLayout *formLayout = new QFormLayout(form);
+ formLayout->addRow(PROJECT_NAME_LABEL, m_nameEditor);
+ formLayout->addRow(PARENT_DIR_LABEL, m_dirSelector);
+
+ QVBoxLayout *dialogLayout = new QVBoxLayout(this);
+ dialogLayout->addWidget(mainLabel);
+ dialogLayout->addWidget(form);
+ dialogLayout->addWidget(m_errorLabel);
+ dialogLayout->addWidget(buttons);
+
+ pathValidChanged();
+}
+
+void CmakeProjectConverterDialog::pathValidChanged()
+{
+ bool valid = isValid();
+
+ if (valid) {
+ m_newProjectDir = FilePath::fromString(m_dirSelector->path()).pathAppended(m_nameEditor->text());
+ }
+ else {
+ m_newProjectDir = FilePath();
+ }
+
+ const QString error = errorText();
+ m_errorLabel->setType(error.isEmpty() ? InfoLabel::None : InfoLabel::Warning);
+ m_errorLabel->setText(error);
+ m_okButton->setEnabled(valid);
+}
+
+const FilePath CmakeProjectConverterDialog::newPath() const
+{
+ return m_newProjectDir;
+}
+
+const QStringList blackListedStarts = {"import","QtQml","QtQuick"};
+
+const QString CmakeProjectConverterDialog::startsWithBlacklisted(const QString &text) const
+{
+ for (const QString &badWord : blackListedStarts) {
+ if (text.startsWith(badWord))
+ return badWord;
+ }
+
+ return {};
+}
+
+const QString ERROR_TEXT_NAME_EMPTY = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Name is empty.");
+const QString ERROR_TEXT_NAME_BAD_START = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Name must not start with \"%1\".");
+const QString ERROR_TEXT_NAME_LOWERCASE_START = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Name must begin with a capital letter");
+const QString ERROR_TEXT_NAME_BAD_CHARACTERS = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Name must contain only letters, numbers or characters - _.");
+
+const QString ERROR_DIR_NOT_DIR = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Target is not a directory.");
+const QString ERROR_DIR_NOT_WRITABLE = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Cannot write to target directory.");
+const QString ERROR_DIR_EXISTS = QCoreApplication::translate(
+ "QmlDesigner::CmakeProjectConverterDialog",
+ "Project directory already exists.");
+
+const QString CmakeProjectConverterDialog::errorText() const
+{
+ QString text;
+
+ if (!m_nameEditor->isValid()) {
+ QString name = m_nameEditor->text();
+
+ if (name.isEmpty())
+ return ERROR_TEXT_NAME_EMPTY;
+
+ const QString badStart = startsWithBlacklisted(text);
+ if (!badStart.isEmpty())
+ return ERROR_TEXT_NAME_BAD_START.arg(badStart);
+
+ if (name[0].isLower())
+ return ERROR_TEXT_NAME_LOWERCASE_START;
+
+ return ERROR_TEXT_NAME_BAD_CHARACTERS;
+
+ }
+
+ if (!m_dirSelector->isValid()) {
+ FilePath path = m_dirSelector->filePath();
+ if (!path.isDir())
+ return ERROR_DIR_NOT_DIR;
+ if (!path.isWritableDir())
+ return ERROR_DIR_NOT_WRITABLE;
+ }
+
+ if (FilePath::fromString(m_dirSelector->path()).pathAppended(m_nameEditor->text()).exists())
+ return ERROR_DIR_EXISTS;
+
+
+ return text;
+}
+
+const QString CmakeProjectConverterDialog::uniqueProjectName(const FilePath &dir, const QString &oldName) const
+{
+ for (unsigned i = 0; ; ++i) {
+ QString name = oldName;
+ if (i)
+ name += QString::number(i);
+ if (!dir.pathAppended(name).exists())
+ return name;
+ }
+ return oldName;
+}
+
+bool CmakeProjectConverterDialog::isValid()
+{
+ FilePath newPath = FilePath::fromString(m_dirSelector->path()).pathAppended(m_nameEditor->text());
+ return m_dirSelector->isValid() && m_nameEditor->isValid() && !newPath.exists();
+}
+
+} //GenerateCmake
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/cmakeprojectconverterdialog.h b/src/plugins/qmldesigner/cmakeprojectconverterdialog.h
new file mode 100644
index 00000000000..a3143de7cd8
--- /dev/null
+++ b/src/plugins/qmldesigner/cmakeprojectconverterdialog.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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.
+**
+****************************************************************************/
+
+
+#ifndef CMAKEPROJECTCONVERTERDIALOG_H
+#define CMAKEPROJECTCONVERTERDIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace QmlDesigner {
+namespace GenerateCmake {
+
+class CmakeProjectConverterDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject);
+ const Utils::FilePath newPath() const;
+
+public slots:
+ void pathValidChanged();
+
+private:
+ const QString startsWithBlacklisted(const QString &text) const;
+ const QString errorText() const;
+ const QString uniqueProjectName(const Utils::FilePath &dir, const QString &oldName) const;
+ bool isValid();
+
+private:
+ Utils::FilePath m_newProjectDir;
+ Utils::FancyLineEdit *m_nameEditor;
+ Utils::PathChooser *m_dirSelector;
+ Utils::InfoLabel *m_errorLabel;
+ QPushButton *m_okButton;
+};
+
+} //GenerateCmake
+} //Qmldesigner
+
+#endif // CMAKEPROJECTCONVERTERDIALOG_H
diff --git a/src/plugins/qmldesigner/generatecmakelists.cpp b/src/plugins/qmldesigner/generatecmakelists.cpp
index d4758619ccd..a585f2d2d40 100644
--- a/src/plugins/qmldesigner/generatecmakelists.cpp
+++ b/src/plugins/qmldesigner/generatecmakelists.cpp
@@ -66,24 +66,26 @@ enum ProjectDirectoryError {
NoError = 0,
MissingContentDir = 1<<1,
MissingImportDir = 1<<2,
- MissingAssetImportDir = 1<<3,
- MissingCppDir = 1<<4,
- MissingMainCMake = 1<<5,
- MissingMainQml = 1<<6,
- MissingAppMainQml = 1<<7,
- MissingQmlModules = 1<<8,
- MissingMainCpp = 1<<9,
- MissingMainCppHeader = 1<<10,
- MissingEnvHeader = 1<<11
+ MissingAssetDir = 1<<3,
+ MissingAssetImportDir = 1<<4,
+ MissingCppDir = 1<<5,
+ MissingMainCMake = 1<<6,
+ MissingMainQml = 1<<7,
+ MissingAppMainQml = 1<<8,
+ MissingQmlModules = 1<<9,
+ MissingMainCpp = 1<<10,
+ MissingMainCppHeader = 1<<11,
+ MissingEnvHeader = 1<<12
};
-QVector queuedFiles;
+const QString MENU_ITEM_GENERATE = QCoreApplication::translate("QmlDesigner::GenerateCmake",
+ "Generate CMake Build Files");
void generateMenuEntry()
{
Core::ActionContainer *menu =
Core::ActionManager::actionContainer(Core::Constants::M_FILE);
- auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateCmake", "Export to Qt Creator (CMake)"));
+ auto action = new QAction(MENU_ITEM_GENERATE);
QObject::connect(action, &QAction::triggered, GenerateCmake::onGenerateCmakeLists);
Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists");
menu->addAction(cmd, Core::Constants::G_FILE_EXPORT);
@@ -106,11 +108,19 @@ void onGenerateCmakeLists()
return;
}
- queuedFiles.clear();
- GenerateCmakeLists::generateCmakes(rootDir);
- GenerateEntryPoints::generateEntryPointFiles(rootDir);
- if (showConfirmationDialog(rootDir))
- writeQueuedFiles();
+ CmakeFileGenerator cmakeGen;
+ cmakeGen.prepare(rootDir);
+
+ FilePaths allFiles;
+ for (const GeneratableFile &file: cmakeGen.fileQueue().queuedFiles())
+ allFiles.append(file.filePath);
+
+ CmakeGeneratorDialog dialog(rootDir, allFiles);
+ if (dialog.exec()) {
+ FilePaths confirmedFiles = dialog.getFilePaths();
+ cmakeGen.filterFileQueue(confirmedFiles);
+ cmakeGen.execute();
+ }
}
bool isErrorFatal(int error)
@@ -135,7 +145,7 @@ int isProjectCorrectlyFormed(const FilePath &rootDir)
if (!rootDir.pathAppended(DIRNAME_IMPORT).exists())
errors |= MissingImportDir;
- if (!rootDir.pathAppended(DIRNAME_ASSET).exists())
+ if (!rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists())
errors |= MissingAssetImportDir;
if (!rootDir.pathAppended(DIRNAME_CPP).exists())
@@ -157,13 +167,6 @@ int isProjectCorrectlyFormed(const FilePath &rootDir)
return errors;
}
-void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles)
-{
- QtConcurrent::blockingFilter(queuedFiles, [confirmedFiles](const GeneratableFile &qf) {
- return confirmedFiles.contains(qf.filePath);
- });
-}
-
const QString WARNING_MISSING_STRUCTURE_FATAL = QCoreApplication::translate("QmlDesigner::GenerateCmake",
"The project is not properly structured for automatically generating CMake files.\n\nAborting process.\n\nThe following files or directories are missing:\n\n%1");
//const QString WARNING_MISSING_STRUCTURE_NONFATAL = QCoreApplication::translate("QmlDesigner::GenerateCmake",
@@ -198,44 +201,32 @@ void showProjectDirErrorDialog(int error)
}
}
-bool showConfirmationDialog(const Utils::FilePath &rootDir)
-{
- Utils::FilePaths files;
- for (GeneratableFile &file: queuedFiles)
- files.append(file.filePath);
-
- CmakeGeneratorDialog dialog(rootDir, files);
- if (dialog.exec()) {
- Utils::FilePaths confirmedFiles = dialog.getFilePaths();
- removeUnconfirmedQueuedFiles(confirmedFiles);
-
- return true;
- }
-
- return false;
-}
-
-bool queueFile(const FilePath &filePath, const QString &fileContent)
+bool FileQueue::queueFile(const FilePath &filePath, const QString &fileContent)
{
GeneratableFile file;
file.filePath = filePath;
file.content = fileContent;
file.fileExists = filePath.exists();
- queuedFiles.append(file);
+ m_queuedFiles.append(file);
return true;
}
-bool writeQueuedFiles()
+const QVector FileQueue::queuedFiles() const
{
- for (GeneratableFile &file: queuedFiles)
+ return m_queuedFiles;
+}
+
+bool FileQueue::writeQueuedFiles()
+{
+ for (GeneratableFile &file: m_queuedFiles)
if (!writeFile(file))
return false;
return true;
}
-bool writeFile(const GeneratableFile &file)
+bool FileQueue::writeFile(const GeneratableFile &file)
{
QFile fileHandle(file.filePath.toString());
fileHandle.open(QIODevice::WriteOnly);
@@ -246,6 +237,13 @@ bool writeFile(const GeneratableFile &file)
return true;
}
+void FileQueue::filterFiles(const Utils::FilePaths keepFiles)
+{
+ QtConcurrent::blockingFilter(m_queuedFiles, [keepFiles](const GeneratableFile &qf) {
+ return keepFiles.contains(qf.filePath);
+ });
+}
+
QString readTemplate(const QString &templatePath)
{
QFile templatefile(templatePath);
@@ -257,54 +255,90 @@ QString readTemplate(const QString &templatePath)
return content;
}
+const QString projectEnvironmentVariable(const QString &key)
+{
+ QString value = {};
+
+ auto *target = ProjectExplorer::SessionManager::startupProject()->activeTarget();
+ if (target && target->buildSystem()) {
+ auto buildSystem = qobject_cast(target->buildSystem());
+ if (buildSystem) {
+ auto envItems = buildSystem->environment();
+ auto confEnv = std::find_if(envItems.begin(), envItems.end(),
+ [key](NameValueItem &item){return item.name == key;});
+ if (confEnv != envItems.end())
+ value = confEnv->value;
+ }
+ }
+ return value;
}
-namespace GenerateCmakeLists {
-
-QStringList moduleNames;
-
const QDir::Filters FILES_ONLY = QDir::Files;
const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot;
const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl";
const char QMLMODULES_FILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodules.tpl";
-bool generateCmakes(const FilePath &rootDir)
+bool CmakeFileGenerator::prepare(const FilePath &rootDir, bool checkFileBelongs)
{
- moduleNames.clear();
+ m_checkFileIsInProject = checkFileBelongs;
FilePath contentDir = rootDir.pathAppended(DIRNAME_CONTENT);
FilePath importDir = rootDir.pathAppended(DIRNAME_IMPORT);
- FilePath assetDir = rootDir.pathAppended(DIRNAME_ASSET);
+ FilePath assetImportDir = rootDir.pathAppended(DIRNAME_ASSETIMPORT);
generateModuleCmake(contentDir);
generateImportCmake(importDir);
- generateImportCmake(assetDir);
+ generateImportCmake(assetImportDir);
generateMainCmake(rootDir);
+ generateEntryPointFiles(rootDir);
return true;
}
+const FileQueue CmakeFileGenerator::fileQueue() const
+{
+ return m_fileQueue;
+}
+
+void CmakeFileGenerator::filterFileQueue(const Utils::FilePaths &keepFiles)
+{
+ m_fileQueue.filterFiles(keepFiles);
+}
+
+bool CmakeFileGenerator::execute()
+{
+ return m_fileQueue.writeQueuedFiles();
+}
+
const char DO_NOT_EDIT_FILE_COMMENT[] = "### This file is automatically generated by Qt Design Studio.\n### Do not change\n\n";
const char ADD_SUBDIR[] = "add_subdirectory(%1)\n";
-void generateMainCmake(const FilePath &rootDir)
+void CmakeFileGenerator::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();
QString appName = projectName + "App";
- QString cmakeFileContent = GenerateCmake::readTemplate(MAIN_CMAKEFILE_TEMPLATE_PATH).arg(appName);
+ QString fileSection = "";
+ const QString qtcontrolsConfFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
+ if (!qtcontrolsConfFile.isEmpty())
+ fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile);
+
+ QString cmakeFileContent = GenerateCmake::readTemplate(MAIN_CMAKEFILE_TEMPLATE_PATH)
+ .arg(appName)
+ .arg(fileSection);
+
queueCmakeFile(rootDir, cmakeFileContent);
QString subdirIncludes;
subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_CONTENT));
subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_IMPORT));
- if (rootDir.pathAppended(DIRNAME_ASSET).exists())
- subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSET));
+ if (rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists())
+ subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSETIMPORT));
QString modulesAsPlugins;
- for (const QString &moduleName : moduleNames)
+ for (const QString &moduleName : m_moduleNames)
modulesAsPlugins.append(" " + moduleName + "plugin\n");
QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH)
@@ -312,10 +346,10 @@ void generateMainCmake(const FilePath &rootDir)
.arg(subdirIncludes)
.arg(modulesAsPlugins);
- GenerateCmake::queueFile(rootDir.pathAppended(FILENAME_MODULES), moduleFileContent);
+ m_fileQueue.queueFile(rootDir.pathAppended(FILENAME_MODULES), moduleFileContent);
}
-void generateImportCmake(const FilePath &dir, const QString &modulePrefix)
+void CmakeFileGenerator::generateImportCmake(const FilePath &dir, const QString &modulePrefix)
{
if (!dir.exists())
return;
@@ -347,7 +381,7 @@ const char MODULEFILE_PROPERTY_SINGLETON[] = "QT_QML_SINGLETON_TYPE";
const char MODULEFILE_PROPERTY_SET[] = "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n";
const char MODULEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodulecmakelists.tpl";
-void generateModuleCmake(const FilePath &dir, const QString &uri)
+void CmakeFileGenerator::generateModuleCmake(const FilePath &dir, const QString &uri)
{
QString fileTemplate = GenerateCmake::readTemplate(MODULEFILE_TEMPLATE_PATH);
@@ -381,15 +415,16 @@ void generateModuleCmake(const FilePath &dir, const QString &uri)
QString moduleUri = uri.isEmpty() ?
dir.fileName() :
uri;
+
QString moduleName = QString(moduleUri).replace('.', '_');
- moduleNames.append(moduleName);
+ m_moduleNames.append(moduleName);
QString fileContent;
fileContent.append(fileTemplate.arg(singletonContent, moduleName, moduleUri, moduleContent));
queueCmakeFile(dir, fileContent);
}
-QStringList getSingletonsFromQmldirFile(const FilePath &filePath)
+QStringList CmakeFileGenerator::getSingletonsFromQmldirFile(const FilePath &filePath)
{
QStringList singletons;
QFile f(filePath.toString());
@@ -412,36 +447,42 @@ QStringList getSingletonsFromQmldirFile(const FilePath &filePath)
return singletons;
}
-FilePaths getDirectoryQmls(const FilePath &dir)
+QStringList CmakeFileGenerator::getDirectoryQmls(const FilePath &dir)
{
- const QStringList qmlFilesOnly("*.qml");
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ QStringList moduleFiles;
+
+ const QStringList qmlFilesOnly(FILENAME_FILTER_QML);
FilePaths allFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY});
- FilePaths moduleFiles;
for (FilePath &file : allFiles) {
- if (!isFileBlacklisted(file.fileName()) &&
- project->isKnownFile(file)) {
- moduleFiles.append(file);
+ if (includeFile(file)) {
+ moduleFiles.append(file.fileName());
}
}
return moduleFiles;
}
-QStringList getDirectoryTreeQmls(const FilePath &dir)
+QStringList CmakeFileGenerator::getDirectoryResources(const FilePath &dir)
{
- const QStringList qmlFilesOnly("*.qml");
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
- QStringList qmlFileList;
+ QStringList moduleFiles;
- FilePaths thisDirFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY});
- for (FilePath &file : thisDirFiles) {
- if (!isFileBlacklisted(file.fileName()) &&
- project->isKnownFile(file)) {
- qmlFileList.append(file.fileName());
+ FilePaths allFiles = dir.dirEntries(FILES_ONLY);
+ for (FilePath &file : allFiles) {
+ if (!file.fileName().endsWith(".qml", Qt::CaseInsensitive) &&
+ includeFile(file)) {
+ moduleFiles.append(file.fileName());
}
}
+ return moduleFiles;
+}
+
+QStringList CmakeFileGenerator::getDirectoryTreeQmls(const FilePath &dir)
+{
+ QStringList qmlFileList;
+
+ qmlFileList.append(getDirectoryQmls(dir));
+
FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
for (FilePath &subDir : subDirsList) {
if (isDirBlacklisted(subDir))
@@ -455,19 +496,11 @@ QStringList getDirectoryTreeQmls(const FilePath &dir)
return qmlFileList;
}
-QStringList getDirectoryTreeResources(const FilePath &dir)
+QStringList CmakeFileGenerator::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());
- }
- }
+ resourceFileList.append(getDirectoryResources(dir));
FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
for (FilePath &subDir : subDirsList) {
@@ -483,28 +516,41 @@ QStringList getDirectoryTreeResources(const FilePath &dir)
return resourceFileList;
}
-void queueCmakeFile(const FilePath &dir, const QString &content)
+void CmakeFileGenerator::queueCmakeFile(const FilePath &dir, const QString &content)
{
FilePath filePath = dir.pathAppended(FILENAME_CMAKELISTS);
- GenerateCmake::queueFile(filePath, content);
+ m_fileQueue.queueFile(filePath, content);
}
-bool isFileBlacklisted(const QString &fileName)
+bool CmakeFileGenerator::isFileBlacklisted(const QString &fileName)
{
return (!fileName.compare(FILENAME_QMLDIR) ||
!fileName.compare(FILENAME_CMAKELISTS));
}
-bool isDirBlacklisted(const FilePath &dir)
+bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir)
{
return (!dir.fileName().compare(DIRNAME_DESIGNER));
}
+bool CmakeFileGenerator::includeFile(const FilePath &filePath)
+{
+ if (m_checkFileIsInProject) {
+ ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ if (!project->isKnownFile(filePath))
+ return false;
+ }
+
+ return !isFileBlacklisted(filePath.fileName());
}
-namespace GenerateEntryPoints {
-bool generateEntryPointFiles(const FilePath &dir)
+
+bool CmakeFileGenerator::generateEntryPointFiles(const FilePath &dir)
{
+ const QString qtcontrolsConf = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
+ if (!qtcontrolsConf.isEmpty())
+ m_resourceFileLocations.append(qtcontrolsConf);
+
bool cppOk = generateMainCpp(dir);
bool qmlOk = generateMainQml(dir);
@@ -517,23 +563,23 @@ const char MAIN_CPPFILE_HEADER_PLUGIN_LINE[] = "Q_IMPORT_QML_PLUGIN(%1)\n";
const char ENV_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl";
const char ENV_HEADER_VARIABLE_LINE[] = " qputenv(\"%1\", \"%2\");\n";
-bool generateMainCpp(const FilePath &dir)
+bool CmakeFileGenerator::generateMainCpp(const FilePath &dir)
{
FilePath srcDir = dir.pathAppended(DIRNAME_CPP);
QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH);
FilePath cppFilePath = srcDir.pathAppended(FILENAME_MAINCPP);
- bool cppOk = GenerateCmake::queueFile(cppFilePath, cppContent);
+ bool cppOk = m_fileQueue.queueFile(cppFilePath, cppContent);
QString modulesAsPlugins;
- for (const QString &moduleName : GenerateCmakeLists::moduleNames)
+ for (const QString &moduleName : m_moduleNames)
modulesAsPlugins.append(
QString(MAIN_CPPFILE_HEADER_PLUGIN_LINE).arg(moduleName + "Plugin"));
QString headerContent = GenerateCmake::readTemplate(MAIN_CPPFILE_HEADER_TEMPLATE_PATH)
.arg(modulesAsPlugins);
FilePath headerFilePath = srcDir.pathAppended(FILENAME_MAINCPP_HEADER);
- bool pluginHeaderOk = GenerateCmake::queueFile(headerFilePath, headerContent);
+ bool pluginHeaderOk = m_fileQueue.queueFile(headerFilePath, headerContent);
bool envHeaderOk = true;
QString environment;
@@ -551,7 +597,7 @@ bool generateMainCpp(const FilePath &dir)
QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH)
.arg(environment);
FilePath envHeaderPath = srcDir.pathAppended(FILENAME_ENV_HEADER);
- envHeaderOk = GenerateCmake::queueFile(envHeaderPath, envHeaderContent);
+ envHeaderOk = m_fileQueue.queueFile(envHeaderPath, envHeaderContent);
}
}
@@ -560,24 +606,21 @@ bool generateMainCpp(const FilePath &dir)
const char MAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmainqml.tpl";
-bool generateMainQml(const FilePath &dir)
+bool CmakeFileGenerator::generateMainQml(const FilePath &dir)
{
QString content = GenerateCmake::readTemplate(MAIN_QMLFILE_TEMPLATE_PATH);
FilePath filePath = dir.pathAppended(FILENAME_MAINQML);
- return GenerateCmake::queueFile(filePath, content);
+ return m_fileQueue.queueFile(filePath, content);
}
-const QStringList resourceFileLocations = {"qtquickcontrols2.conf"};
-
-bool isFileResource(const QString &relativeFilePath)
+bool CmakeFileGenerator::isFileResource(const QString &relativeFilePath)
{
- if (resourceFileLocations.contains(relativeFilePath))
+ if (m_resourceFileLocations.contains(relativeFilePath))
return true;
return false;
}
-} //GenerateEntryPoints
-
+} //GenerateCmake
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/generatecmakelists.h b/src/plugins/qmldesigner/generatecmakelists.h
index eafe72defcb..7045ce17c8a 100644
--- a/src/plugins/qmldesigner/generatecmakelists.h
+++ b/src/plugins/qmldesigner/generatecmakelists.h
@@ -43,31 +43,56 @@ void generateMenuEntry();
void onGenerateCmakeLists();
bool isErrorFatal(int error);
int isProjectCorrectlyFormed(const Utils::FilePath &rootDir);
-void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles);
void showProjectDirErrorDialog(int error);
-bool showConfirmationDialog(const Utils::FilePath &rootDir);
-bool queueFile(const Utils::FilePath &filePath, const QString &fileContent);
-bool writeFile(const GeneratableFile &file);
-bool writeQueuedFiles();
QString readTemplate(const QString &templatePath);
-}
-namespace GenerateCmakeLists {
-bool generateCmakes(const Utils::FilePath &rootDir);
-void generateMainCmake(const Utils::FilePath &rootDir);
-void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString());
-void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString());
-Utils::FilePaths getDirectoryQmls(const Utils::FilePath &dir);
-QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath);
-QStringList getDirectoryTreeQmls(const Utils::FilePath &dir);
-QStringList getDirectoryTreeResources(const Utils::FilePath &dir);
-void queueCmakeFile(const Utils::FilePath &filePath, const QString &content);
-bool isFileBlacklisted(const QString &fileName);
-bool isDirBlacklisted(const Utils::FilePath &dir);
-}
-namespace GenerateEntryPoints {
-bool generateEntryPointFiles(const Utils::FilePath &dir);
-bool generateMainCpp(const Utils::FilePath &dir);
-bool generateMainQml(const Utils::FilePath &dir);
-bool isFileResource(const QString &relativeFilePath);
-}
-}
+const QString projectEnvironmentVariable(const QString &key);
+
+class FileQueue {
+public:
+ bool queueFile(const Utils::FilePath &filePath, const QString &fileContent);
+ const QVector queuedFiles() const;
+ bool writeQueuedFiles();
+ void filterFiles(const Utils::FilePaths keepFiles);
+
+private:
+ bool writeFile(const GeneratableFile &file);
+
+private:
+ QVector m_queuedFiles;
+};
+
+class CmakeFileGenerator {
+public:
+ bool prepare(const Utils::FilePath &rootDir, bool check = true);
+ const FileQueue fileQueue() const;
+ void filterFileQueue(const Utils::FilePaths &keepFiles);
+ bool execute();
+
+private:
+ void generateMainCmake(const Utils::FilePath &rootDir);
+ void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString());
+ void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString());
+ bool generateEntryPointFiles(const Utils::FilePath &dir);
+ bool generateMainCpp(const Utils::FilePath &dir);
+ bool generateMainQml(const Utils::FilePath &dir);
+ QStringList getDirectoryQmls(const Utils::FilePath &dir);
+ QStringList getDirectoryResources(const Utils::FilePath &dir);
+ QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath);
+ QStringList getDirectoryTreeQmls(const Utils::FilePath &dir);
+ QStringList getDirectoryTreeResources(const Utils::FilePath &dir);
+ void queueCmakeFile(const Utils::FilePath &filePath, const QString &content);
+ bool isFileResource(const QString &relativeFilePath);
+ bool isFileBlacklisted(const QString &fileName);
+ bool isDirBlacklisted(const Utils::FilePath &dir);
+ bool includeFile(const Utils::FilePath &filePath);
+
+private:
+ FileQueue m_fileQueue;
+ QStringList m_resourceFileLocations;
+ QStringList m_moduleNames;
+ bool m_checkFileIsInProject;
+};
+
+} //GenerateCmake
+
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/generatecmakelistsconstants.h b/src/plugins/qmldesigner/generatecmakelistsconstants.h
index 0374ac2ad65..e4358dc5999 100644
--- a/src/plugins/qmldesigner/generatecmakelistsconstants.h
+++ b/src/plugins/qmldesigner/generatecmakelistsconstants.h
@@ -34,7 +34,8 @@ namespace Constants {
const char DIRNAME_CONTENT[] = "content";
const char DIRNAME_IMPORT[] = "imports";
-const char DIRNAME_ASSET[] = "asset_imports";
+const char DIRNAME_ASSET[] = "assets";
+const char DIRNAME_ASSETIMPORT[] = "asset_imports";
const char DIRNAME_CPP[] = "src";
const char DIRNAME_DESIGNER[] = "designer";
@@ -47,6 +48,15 @@ const char FILENAME_MODULES[] = "qmlmodules";
const char FILENAME_QMLDIR[] = "qmldir";
const char FILENAME_ENV_HEADER[] = "app_environment.h";
+const char FILENAME_SUFFIX_QMLPROJECT[] = "qmlproject";
+const char FILENAME_SUFFIX_QML[] = "qml";
+const char FILENAME_SUFFIX_USER[] = "user";
+
+const char FILENAME_FILTER_QMLPROJECT[] = "*.qmlproject";
+const char FILENAME_FILTER_QML[] = "*.qml";
+
+const char ENV_VARIABLE_CONTROLCONF[] = "QT_QUICK_CONTROLS_CONF";
+
} //Constants
} //GenerateCmake
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 979a0321f26..3748a2164e0 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -24,6 +24,7 @@
****************************************************************************/
#include "qmldesignerplugin.h"
+#include "cmakeprojectconverter.h"
#include "designmodecontext.h"
#include "designmodewidget.h"
#include "exception.h"
@@ -230,6 +231,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
GenerateResource::generateMenuEntry();
GenerateCmake::generateMenuEntry();
+ GenerateCmake::CmakeProjectConverter::generateMenuEntry();
const QString fontPath
= Core::ICore::resourcePath(
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs
index a3f5649680c..4ab3c754c9f 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.qbs
+++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs
@@ -1035,6 +1035,8 @@ Project {
"cmakegeneratordialogtreemodel.h",
"cmakegeneratordialog.cpp",
"cmakegeneratordialog.h",
+ "cmakeprojectconverter.cpp",
+ "cmakeprojectconverter.h",
"designersettings.cpp",
"designersettings.h",
"designmodecontext.cpp",
diff --git a/src/plugins/qmldesigner/qmlprojectappmainqml.tpl b/src/plugins/qmldesigner/qmlprojectappmainqml.tpl
new file mode 100644
index 00000000000..35f8218dc65
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlprojectappmainqml.tpl
@@ -0,0 +1,14 @@
+import QtQuick
+import QtQuick.Window
+
+Window {
+ visible: true
+ title: "%1"
+ width: mainScreen.width
+ height: mainScreen.height
+
+ %1 {
+ id: mainScreen
+ }
+
+}
diff --git a/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl b/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl
index 14a91d20eaf..423ed1a921a 100644
--- a/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl
+++ b/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl
@@ -10,8 +10,7 @@ qt_add_executable(%1 src/main.cpp)
qt_add_resources(%1 "configuration"
PREFIX "/"
- FILES
- qtquickcontrols2.conf
+%2
)
target_link_libraries(%1 PRIVATE