Convert old projects to project that supports the cmake generation workflow

Task-number: QDS-5992
Change-Id: I9dd4204d7422052c80c86665ded558d959bb020b
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Thomas Hartmann
2022-02-18 20:22:19 +01:00
parent a0d19e088e
commit c82937a78c
14 changed files with 1021 additions and 140 deletions

View File

@@ -30,6 +30,8 @@ add_qtc_plugin(QmlDesigner
checkablefiletreeitem.cpp checkablefiletreeitem.h checkablefiletreeitem.cpp checkablefiletreeitem.h
cmakegeneratordialog.cpp cmakegeneratordialog.h cmakegeneratordialog.cpp cmakegeneratordialog.h
cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h
cmakeprojectconverter.cpp cmakeprojectconverter.h
cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h
generateresource.cpp generateresource.h generateresource.cpp generateresource.h
generatecmakelists.cpp generatecmakelists.h generatecmakelists.cpp generatecmakelists.h
generatecmakelistsconstants.h generatecmakelistsconstants.h

View File

@@ -7,5 +7,6 @@
<file>qmlprojectmaincmakelists.tpl</file> <file>qmlprojectmaincmakelists.tpl</file>
<file>qmlprojectmodulecmakelists.tpl</file> <file>qmlprojectmodulecmakelists.tpl</file>
<file>qmlprojectmainqml.tpl</file> <file>qmlprojectmainqml.tpl</file>
<file>qmlprojectappmainqml.tpl</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -151,5 +151,5 @@ void CmakeGeneratorDialog::refreshNotificationText()
} }
} }
} } //GenerateCmake
} } //QmlDesigner

View File

@@ -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 <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <QAction>
#include <QMessageBox>
#include <QRegularExpression>
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<const QmlProjectManager::QmlProject*>(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<QmlProjectManager::QmlBuildSystem *>(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

View File

@@ -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 <utils/fileutils.h>
#include <qmlprojectmanager/qmlproject.h>
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<ProjectConverterObject> 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

View File

@@ -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 <coreplugin/documentmanager.h>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QPushButton>
#include <QRegularExpression>
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

View File

@@ -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 <qmlprojectmanager/qmlproject.h>
#include <utils/fancylineedit.h>
#include <utils/filepath.h>
#include <utils/infolabel.h>
#include <utils/pathchooser.h>
#include <QDialog>
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

View File

@@ -66,24 +66,26 @@ enum ProjectDirectoryError {
NoError = 0, NoError = 0,
MissingContentDir = 1<<1, MissingContentDir = 1<<1,
MissingImportDir = 1<<2, MissingImportDir = 1<<2,
MissingAssetImportDir = 1<<3, MissingAssetDir = 1<<3,
MissingCppDir = 1<<4, MissingAssetImportDir = 1<<4,
MissingMainCMake = 1<<5, MissingCppDir = 1<<5,
MissingMainQml = 1<<6, MissingMainCMake = 1<<6,
MissingAppMainQml = 1<<7, MissingMainQml = 1<<7,
MissingQmlModules = 1<<8, MissingAppMainQml = 1<<8,
MissingMainCpp = 1<<9, MissingQmlModules = 1<<9,
MissingMainCppHeader = 1<<10, MissingMainCpp = 1<<10,
MissingEnvHeader = 1<<11 MissingMainCppHeader = 1<<11,
MissingEnvHeader = 1<<12
}; };
QVector<GeneratableFile> queuedFiles; const QString MENU_ITEM_GENERATE = QCoreApplication::translate("QmlDesigner::GenerateCmake",
"Generate CMake Build Files");
void generateMenuEntry() void generateMenuEntry()
{ {
Core::ActionContainer *menu = Core::ActionContainer *menu =
Core::ActionManager::actionContainer(Core::Constants::M_FILE); 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); QObject::connect(action, &QAction::triggered, GenerateCmake::onGenerateCmakeLists);
Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists"); Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists");
menu->addAction(cmd, Core::Constants::G_FILE_EXPORT); menu->addAction(cmd, Core::Constants::G_FILE_EXPORT);
@@ -106,11 +108,19 @@ void onGenerateCmakeLists()
return; return;
} }
queuedFiles.clear(); CmakeFileGenerator cmakeGen;
GenerateCmakeLists::generateCmakes(rootDir); cmakeGen.prepare(rootDir);
GenerateEntryPoints::generateEntryPointFiles(rootDir);
if (showConfirmationDialog(rootDir)) FilePaths allFiles;
writeQueuedFiles(); 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) bool isErrorFatal(int error)
@@ -135,7 +145,7 @@ int isProjectCorrectlyFormed(const FilePath &rootDir)
if (!rootDir.pathAppended(DIRNAME_IMPORT).exists()) if (!rootDir.pathAppended(DIRNAME_IMPORT).exists())
errors |= MissingImportDir; errors |= MissingImportDir;
if (!rootDir.pathAppended(DIRNAME_ASSET).exists()) if (!rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists())
errors |= MissingAssetImportDir; errors |= MissingAssetImportDir;
if (!rootDir.pathAppended(DIRNAME_CPP).exists()) if (!rootDir.pathAppended(DIRNAME_CPP).exists())
@@ -157,13 +167,6 @@ int isProjectCorrectlyFormed(const FilePath &rootDir)
return errors; 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", 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"); "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", //const QString WARNING_MISSING_STRUCTURE_NONFATAL = QCoreApplication::translate("QmlDesigner::GenerateCmake",
@@ -198,44 +201,32 @@ void showProjectDirErrorDialog(int error)
} }
} }
bool showConfirmationDialog(const Utils::FilePath &rootDir) bool FileQueue::queueFile(const FilePath &filePath, const QString &fileContent)
{
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)
{ {
GeneratableFile file; GeneratableFile file;
file.filePath = filePath; file.filePath = filePath;
file.content = fileContent; file.content = fileContent;
file.fileExists = filePath.exists(); file.fileExists = filePath.exists();
queuedFiles.append(file); m_queuedFiles.append(file);
return true; return true;
} }
bool writeQueuedFiles() const QVector<GeneratableFile> FileQueue::queuedFiles() const
{ {
for (GeneratableFile &file: queuedFiles) return m_queuedFiles;
}
bool FileQueue::writeQueuedFiles()
{
for (GeneratableFile &file: m_queuedFiles)
if (!writeFile(file)) if (!writeFile(file))
return false; return false;
return true; return true;
} }
bool writeFile(const GeneratableFile &file) bool FileQueue::writeFile(const GeneratableFile &file)
{ {
QFile fileHandle(file.filePath.toString()); QFile fileHandle(file.filePath.toString());
fileHandle.open(QIODevice::WriteOnly); fileHandle.open(QIODevice::WriteOnly);
@@ -246,6 +237,13 @@ bool writeFile(const GeneratableFile &file)
return true; 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) QString readTemplate(const QString &templatePath)
{ {
QFile templatefile(templatePath); QFile templatefile(templatePath);
@@ -257,11 +255,23 @@ QString readTemplate(const QString &templatePath)
return content; return content;
} }
const QString projectEnvironmentVariable(const QString &key)
{
QString value = {};
auto *target = ProjectExplorer::SessionManager::startupProject()->activeTarget();
if (target && target->buildSystem()) {
auto buildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(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 FILES_ONLY = QDir::Files;
const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot; const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot;
@@ -269,42 +279,66 @@ const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot;
const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl"; const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl";
const char QMLMODULES_FILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodules.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 contentDir = rootDir.pathAppended(DIRNAME_CONTENT);
FilePath importDir = rootDir.pathAppended(DIRNAME_IMPORT); FilePath importDir = rootDir.pathAppended(DIRNAME_IMPORT);
FilePath assetDir = rootDir.pathAppended(DIRNAME_ASSET); FilePath assetImportDir = rootDir.pathAppended(DIRNAME_ASSETIMPORT);
generateModuleCmake(contentDir); generateModuleCmake(contentDir);
generateImportCmake(importDir); generateImportCmake(importDir);
generateImportCmake(assetDir); generateImportCmake(assetImportDir);
generateMainCmake(rootDir); generateMainCmake(rootDir);
generateEntryPointFiles(rootDir);
return true; 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 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"; 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. //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 projectName = ProjectExplorer::SessionManager::startupProject()->displayName();
QString appName = projectName + "App"; 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); queueCmakeFile(rootDir, cmakeFileContent);
QString subdirIncludes; QString subdirIncludes;
subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_CONTENT)); subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_CONTENT));
subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_IMPORT)); subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_IMPORT));
if (rootDir.pathAppended(DIRNAME_ASSET).exists()) if (rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists())
subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSET)); subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSETIMPORT));
QString modulesAsPlugins; QString modulesAsPlugins;
for (const QString &moduleName : moduleNames) for (const QString &moduleName : m_moduleNames)
modulesAsPlugins.append(" " + moduleName + "plugin\n"); modulesAsPlugins.append(" " + moduleName + "plugin\n");
QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH) QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH)
@@ -312,10 +346,10 @@ void generateMainCmake(const FilePath &rootDir)
.arg(subdirIncludes) .arg(subdirIncludes)
.arg(modulesAsPlugins); .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()) if (!dir.exists())
return; 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_PROPERTY_SET[] = "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n";
const char MODULEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodulecmakelists.tpl"; 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); QString fileTemplate = GenerateCmake::readTemplate(MODULEFILE_TEMPLATE_PATH);
@@ -381,15 +415,16 @@ void generateModuleCmake(const FilePath &dir, const QString &uri)
QString moduleUri = uri.isEmpty() ? QString moduleUri = uri.isEmpty() ?
dir.fileName() : dir.fileName() :
uri; uri;
QString moduleName = QString(moduleUri).replace('.', '_'); QString moduleName = QString(moduleUri).replace('.', '_');
moduleNames.append(moduleName); m_moduleNames.append(moduleName);
QString fileContent; QString fileContent;
fileContent.append(fileTemplate.arg(singletonContent, moduleName, moduleUri, moduleContent)); fileContent.append(fileTemplate.arg(singletonContent, moduleName, moduleUri, moduleContent));
queueCmakeFile(dir, fileContent); queueCmakeFile(dir, fileContent);
} }
QStringList getSingletonsFromQmldirFile(const FilePath &filePath) QStringList CmakeFileGenerator::getSingletonsFromQmldirFile(const FilePath &filePath)
{ {
QStringList singletons; QStringList singletons;
QFile f(filePath.toString()); QFile f(filePath.toString());
@@ -412,35 +447,41 @@ QStringList getSingletonsFromQmldirFile(const FilePath &filePath)
return singletons; return singletons;
} }
FilePaths getDirectoryQmls(const FilePath &dir) QStringList CmakeFileGenerator::getDirectoryQmls(const FilePath &dir)
{ {
const QStringList qmlFilesOnly("*.qml"); QStringList moduleFiles;
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
const QStringList qmlFilesOnly(FILENAME_FILTER_QML);
FilePaths allFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY}); FilePaths allFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY});
FilePaths moduleFiles;
for (FilePath &file : allFiles) { for (FilePath &file : allFiles) {
if (!isFileBlacklisted(file.fileName()) && if (includeFile(file)) {
project->isKnownFile(file)) { moduleFiles.append(file.fileName());
moduleFiles.append(file);
} }
} }
return moduleFiles; return moduleFiles;
} }
QStringList getDirectoryTreeQmls(const FilePath &dir) QStringList CmakeFileGenerator::getDirectoryResources(const FilePath &dir)
{
QStringList moduleFiles;
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)
{ {
const QStringList qmlFilesOnly("*.qml");
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
QStringList qmlFileList; QStringList qmlFileList;
FilePaths thisDirFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY}); qmlFileList.append(getDirectoryQmls(dir));
for (FilePath &file : thisDirFiles) {
if (!isFileBlacklisted(file.fileName()) &&
project->isKnownFile(file)) {
qmlFileList.append(file.fileName());
}
}
FilePaths subDirsList = dir.dirEntries(DIRS_ONLY); FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
for (FilePath &subDir : subDirsList) { for (FilePath &subDir : subDirsList) {
@@ -455,19 +496,11 @@ QStringList getDirectoryTreeQmls(const FilePath &dir)
return qmlFileList; return qmlFileList;
} }
QStringList getDirectoryTreeResources(const FilePath &dir) QStringList CmakeFileGenerator::getDirectoryTreeResources(const FilePath &dir)
{ {
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
QStringList resourceFileList; QStringList resourceFileList;
FilePaths thisDirFiles = dir.dirEntries(FILES_ONLY); resourceFileList.append(getDirectoryResources(dir));
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); FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
for (FilePath &subDir : subDirsList) { for (FilePath &subDir : subDirsList) {
@@ -483,28 +516,41 @@ QStringList getDirectoryTreeResources(const FilePath &dir)
return resourceFileList; 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); 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) || return (!fileName.compare(FILENAME_QMLDIR) ||
!fileName.compare(FILENAME_CMAKELISTS)); !fileName.compare(FILENAME_CMAKELISTS));
} }
bool isDirBlacklisted(const FilePath &dir) bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir)
{ {
return (!dir.fileName().compare(DIRNAME_DESIGNER)); 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;
} }
namespace GenerateEntryPoints { return !isFileBlacklisted(filePath.fileName());
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 cppOk = generateMainCpp(dir);
bool qmlOk = generateMainQml(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_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl";
const char ENV_HEADER_VARIABLE_LINE[] = " qputenv(\"%1\", \"%2\");\n"; 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); FilePath srcDir = dir.pathAppended(DIRNAME_CPP);
QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH); QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH);
FilePath cppFilePath = srcDir.pathAppended(FILENAME_MAINCPP); FilePath cppFilePath = srcDir.pathAppended(FILENAME_MAINCPP);
bool cppOk = GenerateCmake::queueFile(cppFilePath, cppContent); bool cppOk = m_fileQueue.queueFile(cppFilePath, cppContent);
QString modulesAsPlugins; QString modulesAsPlugins;
for (const QString &moduleName : GenerateCmakeLists::moduleNames) for (const QString &moduleName : m_moduleNames)
modulesAsPlugins.append( modulesAsPlugins.append(
QString(MAIN_CPPFILE_HEADER_PLUGIN_LINE).arg(moduleName + "Plugin")); QString(MAIN_CPPFILE_HEADER_PLUGIN_LINE).arg(moduleName + "Plugin"));
QString headerContent = GenerateCmake::readTemplate(MAIN_CPPFILE_HEADER_TEMPLATE_PATH) QString headerContent = GenerateCmake::readTemplate(MAIN_CPPFILE_HEADER_TEMPLATE_PATH)
.arg(modulesAsPlugins); .arg(modulesAsPlugins);
FilePath headerFilePath = srcDir.pathAppended(FILENAME_MAINCPP_HEADER); FilePath headerFilePath = srcDir.pathAppended(FILENAME_MAINCPP_HEADER);
bool pluginHeaderOk = GenerateCmake::queueFile(headerFilePath, headerContent); bool pluginHeaderOk = m_fileQueue.queueFile(headerFilePath, headerContent);
bool envHeaderOk = true; bool envHeaderOk = true;
QString environment; QString environment;
@@ -551,7 +597,7 @@ bool generateMainCpp(const FilePath &dir)
QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH) QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH)
.arg(environment); .arg(environment);
FilePath envHeaderPath = srcDir.pathAppended(FILENAME_ENV_HEADER); 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"; 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); QString content = GenerateCmake::readTemplate(MAIN_QMLFILE_TEMPLATE_PATH);
FilePath filePath = dir.pathAppended(FILENAME_MAINQML); FilePath filePath = dir.pathAppended(FILENAME_MAINQML);
return GenerateCmake::queueFile(filePath, content); return m_fileQueue.queueFile(filePath, content);
} }
const QStringList resourceFileLocations = {"qtquickcontrols2.conf"}; bool CmakeFileGenerator::isFileResource(const QString &relativeFilePath)
bool isFileResource(const QString &relativeFilePath)
{ {
if (resourceFileLocations.contains(relativeFilePath)) if (m_resourceFileLocations.contains(relativeFilePath))
return true; return true;
return false; return false;
} }
} //GenerateEntryPoints } //GenerateCmake
} //QmlDesigner } //QmlDesigner

View File

@@ -43,31 +43,56 @@ void generateMenuEntry();
void onGenerateCmakeLists(); void onGenerateCmakeLists();
bool isErrorFatal(int error); bool isErrorFatal(int error);
int isProjectCorrectlyFormed(const Utils::FilePath &rootDir); int isProjectCorrectlyFormed(const Utils::FilePath &rootDir);
void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles);
void showProjectDirErrorDialog(int error); 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); QString readTemplate(const QString &templatePath);
} const QString projectEnvironmentVariable(const QString &key);
namespace GenerateCmakeLists {
bool generateCmakes(const Utils::FilePath &rootDir); class FileQueue {
public:
bool queueFile(const Utils::FilePath &filePath, const QString &fileContent);
const QVector<GeneratableFile> queuedFiles() const;
bool writeQueuedFiles();
void filterFiles(const Utils::FilePaths keepFiles);
private:
bool writeFile(const GeneratableFile &file);
private:
QVector<GeneratableFile> 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 generateMainCmake(const Utils::FilePath &rootDir);
void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString()); void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString());
void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString()); void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString());
Utils::FilePaths getDirectoryQmls(const Utils::FilePath &dir); 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 getSingletonsFromQmldirFile(const Utils::FilePath &filePath);
QStringList getDirectoryTreeQmls(const Utils::FilePath &dir); QStringList getDirectoryTreeQmls(const Utils::FilePath &dir);
QStringList getDirectoryTreeResources(const Utils::FilePath &dir); QStringList getDirectoryTreeResources(const Utils::FilePath &dir);
void queueCmakeFile(const Utils::FilePath &filePath, const QString &content); void queueCmakeFile(const Utils::FilePath &filePath, const QString &content);
bool isFileResource(const QString &relativeFilePath);
bool isFileBlacklisted(const QString &fileName); bool isFileBlacklisted(const QString &fileName);
bool isDirBlacklisted(const Utils::FilePath &dir); bool isDirBlacklisted(const Utils::FilePath &dir);
} bool includeFile(const Utils::FilePath &filePath);
namespace GenerateEntryPoints {
bool generateEntryPointFiles(const Utils::FilePath &dir); private:
bool generateMainCpp(const Utils::FilePath &dir); FileQueue m_fileQueue;
bool generateMainQml(const Utils::FilePath &dir); QStringList m_resourceFileLocations;
bool isFileResource(const QString &relativeFilePath); QStringList m_moduleNames;
} bool m_checkFileIsInProject;
} };
} //GenerateCmake
} //QmlDesigner

View File

@@ -34,7 +34,8 @@ namespace Constants {
const char DIRNAME_CONTENT[] = "content"; const char DIRNAME_CONTENT[] = "content";
const char DIRNAME_IMPORT[] = "imports"; 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_CPP[] = "src";
const char DIRNAME_DESIGNER[] = "designer"; const char DIRNAME_DESIGNER[] = "designer";
@@ -47,6 +48,15 @@ const char FILENAME_MODULES[] = "qmlmodules";
const char FILENAME_QMLDIR[] = "qmldir"; const char FILENAME_QMLDIR[] = "qmldir";
const char FILENAME_ENV_HEADER[] = "app_environment.h"; 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 } //Constants
} //GenerateCmake } //GenerateCmake
} //QmlDesigner } //QmlDesigner

View File

@@ -24,6 +24,7 @@
****************************************************************************/ ****************************************************************************/
#include "qmldesignerplugin.h" #include "qmldesignerplugin.h"
#include "cmakeprojectconverter.h"
#include "designmodecontext.h" #include "designmodecontext.h"
#include "designmodewidget.h" #include "designmodewidget.h"
#include "exception.h" #include "exception.h"
@@ -230,6 +231,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
GenerateResource::generateMenuEntry(); GenerateResource::generateMenuEntry();
GenerateCmake::generateMenuEntry(); GenerateCmake::generateMenuEntry();
GenerateCmake::CmakeProjectConverter::generateMenuEntry();
const QString fontPath const QString fontPath
= Core::ICore::resourcePath( = Core::ICore::resourcePath(

View File

@@ -1035,6 +1035,8 @@ Project {
"cmakegeneratordialogtreemodel.h", "cmakegeneratordialogtreemodel.h",
"cmakegeneratordialog.cpp", "cmakegeneratordialog.cpp",
"cmakegeneratordialog.h", "cmakegeneratordialog.h",
"cmakeprojectconverter.cpp",
"cmakeprojectconverter.h",
"designersettings.cpp", "designersettings.cpp",
"designersettings.h", "designersettings.h",
"designmodecontext.cpp", "designmodecontext.cpp",

View File

@@ -0,0 +1,14 @@
import QtQuick
import QtQuick.Window
Window {
visible: true
title: "%1"
width: mainScreen.width
height: mainScreen.height
%1 {
id: mainScreen
}
}

View File

@@ -10,8 +10,7 @@ qt_add_executable(%1 src/main.cpp)
qt_add_resources(%1 "configuration" qt_add_resources(%1 "configuration"
PREFIX "/" PREFIX "/"
FILES %2
qtquickcontrols2.conf
) )
target_link_libraries(%1 PRIVATE target_link_libraries(%1 PRIVATE