forked from qt-creator/qt-creator
QmlDesigner: Move CMake generator to QmlProjectManager
Task-number: QDS-6898 Change-Id: Ib1ca2ff4cd17a1ee6c203878a963bf44bac6969b Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
440
src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
Normal file
440
src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <coreplugin/icore.h>
|
||||
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QRegularExpression>
|
||||
|
||||
using namespace Utils;
|
||||
using namespace QmlProjectManager::GenerateCmake::Constants;
|
||||
|
||||
namespace QmlProjectManager {
|
||||
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(isProjectConvertable(ProjectExplorer::SessionManager::startupProject()));
|
||||
QObject::connect(ProjectExplorer::SessionManager::instance(),
|
||||
&ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
|
||||
action->setEnabled(isProjectConvertable(ProjectExplorer::SessionManager::startupProject()));
|
||||
});
|
||||
}
|
||||
|
||||
bool CmakeProjectConverter::isProjectConvertable(const ProjectExplorer::Project *project)
|
||||
{
|
||||
if (!project)
|
||||
return false;
|
||||
|
||||
return !isProjectCurrentFormat(project);
|
||||
}
|
||||
|
||||
const QStringList sanityCheckFiles({FILENAME_CMAKELISTS,
|
||||
FILENAME_MODULES,
|
||||
FILENAME_MAINQML,
|
||||
QString(DIRNAME_CONTENT)+'/'+FILENAME_CMAKELISTS,
|
||||
QString(DIRNAME_IMPORT)+'/'+FILENAME_CMAKELISTS,
|
||||
QString(DIRNAME_CPP)+'/'+FILENAME_MAINCPP,
|
||||
QString(DIRNAME_CPP)+'/'+FILENAME_ENV_HEADER,
|
||||
QString(DIRNAME_CPP)+'/'+FILENAME_MAINCPP_HEADER
|
||||
});
|
||||
|
||||
bool CmakeProjectConverter::isProjectCurrentFormat(const ProjectExplorer::Project *project)
|
||||
{
|
||||
const QmlProjectManager::QmlProject *qmlprj = qobject_cast<const QmlProjectManager::QmlProject*>(project);
|
||||
|
||||
if (!qmlprj)
|
||||
return false;
|
||||
|
||||
FilePath rootDir = qmlprj->rootProjectDirectory();
|
||||
for (const QString &file : sanityCheckFiles)
|
||||
if (!rootDir.pathAppended(file).exists())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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(Core::ICore::dialogParent(), SUCCESS_TITLE, SUCCESS_TEXT);
|
||||
ProjectExplorer::ProjectExplorerPlugin::OpenProjectResult result
|
||||
= ProjectExplorer::ProjectExplorerPlugin::openProject(newProjectFile());
|
||||
if (!result)
|
||||
ProjectExplorer::ProjectExplorerPlugin::showOpenProjectError(result);
|
||||
}
|
||||
else {
|
||||
QMessageBox::critical(Core::ICore::dialogParent(), 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() const
|
||||
{
|
||||
return m_newProjectDir.pathAppended(DIRNAME_CONTENT);
|
||||
}
|
||||
|
||||
const FilePath CmakeProjectConverter::sourceDir() const
|
||||
{
|
||||
return m_newProjectDir.pathAppended(DIRNAME_CPP);
|
||||
}
|
||||
|
||||
const FilePath CmakeProjectConverter::importDir() const
|
||||
{
|
||||
return m_newProjectDir.pathAppended(DIRNAME_IMPORT);
|
||||
}
|
||||
|
||||
const FilePath CmakeProjectConverter::assetDir() const
|
||||
{
|
||||
return contentDir().pathAppended(DIRNAME_ASSET);
|
||||
}
|
||||
|
||||
const FilePath CmakeProjectConverter::assetImportDir() const
|
||||
{
|
||||
return m_newProjectDir.pathAppended(DIRNAME_ASSETIMPORT);
|
||||
}
|
||||
|
||||
const FilePath CmakeProjectConverter::newProjectFile() const
|
||||
{
|
||||
return m_newProjectDir.pathAppended(m_project->projectFilePath().fileName());
|
||||
}
|
||||
|
||||
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::ReadOnly);
|
||||
if (!projectFile.isOpen())
|
||||
return false;
|
||||
QString projectFileContent = QString::fromUtf8(projectFile.readAll());
|
||||
projectFile.close();
|
||||
|
||||
const QRegularExpression mainFilePattern("^\\s*mainFile:\\s*\".*\"", QRegularExpression::MultilineOption);
|
||||
const QString mainFileString(" mainFile: \"content/App.qml\"");
|
||||
|
||||
projectFileContent.replace(mainFilePattern, mainFileString);
|
||||
|
||||
projectFile.open(QIODevice::WriteOnly|QIODevice::Truncate);
|
||||
if (!projectFile.isOpen())
|
||||
return false;
|
||||
projectFile.write(projectFileContent.toUtf8());
|
||||
projectFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} //GenerateCmake
|
||||
} //QmlProjectManager
|
||||
Reference in New Issue
Block a user