forked from qt-creator/qt-creator
CMake generator: Add sanity checks
Task-number: QDS-5410 Change-Id: I8f3cd130d7f5bfac3656e8d006661a81a5412abd Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
@@ -56,13 +57,26 @@ bool operator==(const GeneratableFile &left, const GeneratableFile &right)
|
|||||||
return (left.filePath == right.filePath && left.content == right.content);
|
return (left.filePath == right.filePath && left.content == right.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ProjectDirectoryError {
|
||||||
|
NO_ERROR = 0,
|
||||||
|
MISSING_CONTENTDIR = 1<<1,
|
||||||
|
MISSING_IMPORTDIR = 1<<2,
|
||||||
|
MISSING_CPPDIR = 1<<3,
|
||||||
|
MISSING_MAINCMAKE = 1<<4,
|
||||||
|
MISSING_MAINQML = 1<<5,
|
||||||
|
MISSING_APPMAINQML = 1<<6,
|
||||||
|
MISSING_QMLMODULES = 1<<7,
|
||||||
|
MISSING_MAINCPP = 1<<8,
|
||||||
|
MISSING_MAINCPP_HEADER = 1<<9
|
||||||
|
};
|
||||||
|
|
||||||
QVector<GeneratableFile> queuedFiles;
|
QVector<GeneratableFile> queuedFiles;
|
||||||
|
|
||||||
void generateMenuEntry()
|
void generateMenuEntry()
|
||||||
{
|
{
|
||||||
Core::ActionContainer *buildMenu =
|
Core::ActionContainer *buildMenu =
|
||||||
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
|
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
|
||||||
auto action = new QAction("Generate CMakeLists.txt files");
|
auto action = new QAction(QCoreApplication::tr("Generate CMakeLists.txt Files"));
|
||||||
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");
|
||||||
buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN);
|
buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN);
|
||||||
@@ -76,8 +90,16 @@ void generateMenuEntry()
|
|||||||
|
|
||||||
void onGenerateCmakeLists()
|
void onGenerateCmakeLists()
|
||||||
{
|
{
|
||||||
queuedFiles.clear();
|
|
||||||
FilePath rootDir = ProjectExplorer::SessionManager::startupProject()->projectDirectory();
|
FilePath rootDir = ProjectExplorer::SessionManager::startupProject()->projectDirectory();
|
||||||
|
|
||||||
|
int projectDirErrors = isProjectCorrectlyFormed(rootDir);
|
||||||
|
if (projectDirErrors != NO_ERROR) {
|
||||||
|
showProjectDirErrorDialog(projectDirErrors);
|
||||||
|
if (isErrorFatal(projectDirErrors))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedFiles.clear();
|
||||||
GenerateCmakeLists::generateCmakes(rootDir);
|
GenerateCmakeLists::generateCmakes(rootDir);
|
||||||
GenerateEntryPoints::generateMainCpp(rootDir);
|
GenerateEntryPoints::generateMainCpp(rootDir);
|
||||||
GenerateEntryPoints::generateMainQml(rootDir);
|
GenerateEntryPoints::generateMainQml(rootDir);
|
||||||
@@ -85,6 +107,57 @@ void onGenerateCmakeLists()
|
|||||||
writeQueuedFiles();
|
writeQueuedFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isErrorFatal(int error)
|
||||||
|
{
|
||||||
|
if (error & MISSING_CONTENTDIR ||
|
||||||
|
error & MISSING_IMPORTDIR ||
|
||||||
|
error & MISSING_CPPDIR ||
|
||||||
|
error & MISSING_APPMAINQML)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char DIRNAME_CONTENT[] = "content";
|
||||||
|
const char DIRNAME_IMPORT[] = "imports";
|
||||||
|
const char DIRNAME_CPP[] = "src";
|
||||||
|
|
||||||
|
const char FILENAME_CMAKELISTS[] = "CMakeLists.txt";
|
||||||
|
const char FILENAME_APPMAINQML[] = "App.qml";
|
||||||
|
const char FILENAME_MAINQML[] = "main.qml";
|
||||||
|
const char FILENAME_MAINCPP[] = "main.cpp";
|
||||||
|
const char FILENAME_MAINCPP_HEADER[] = "import_qml_plugins.h";
|
||||||
|
const char FILENAME_MODULES[] = "qmlmodules";
|
||||||
|
|
||||||
|
int isProjectCorrectlyFormed(const FilePath &rootDir)
|
||||||
|
{
|
||||||
|
int errors = NO_ERROR;
|
||||||
|
|
||||||
|
if (!rootDir.pathAppended(DIRNAME_CONTENT).exists())
|
||||||
|
errors |= MISSING_CONTENTDIR;
|
||||||
|
if (!rootDir.pathAppended(DIRNAME_CONTENT).pathAppended(FILENAME_APPMAINQML).exists())
|
||||||
|
errors |= MISSING_APPMAINQML;
|
||||||
|
|
||||||
|
if (!rootDir.pathAppended(DIRNAME_IMPORT).exists())
|
||||||
|
errors |= MISSING_IMPORTDIR;
|
||||||
|
|
||||||
|
if (!rootDir.pathAppended(DIRNAME_CPP).exists())
|
||||||
|
errors |= MISSING_CPPDIR;
|
||||||
|
if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_MAINCPP).exists())
|
||||||
|
errors |= MISSING_MAINCPP;
|
||||||
|
if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_MAINCPP_HEADER).exists())
|
||||||
|
errors |= MISSING_MAINCPP_HEADER;
|
||||||
|
|
||||||
|
if (!rootDir.pathAppended(FILENAME_CMAKELISTS).exists())
|
||||||
|
errors |= MISSING_MAINCMAKE;
|
||||||
|
if (!rootDir.pathAppended(FILENAME_MODULES).exists())
|
||||||
|
errors |= MISSING_QMLMODULES;
|
||||||
|
if (!rootDir.pathAppended(FILENAME_MAINQML).exists())
|
||||||
|
errors |= MISSING_MAINQML;
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles)
|
void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles)
|
||||||
{
|
{
|
||||||
QtConcurrent::blockingFilter(queuedFiles, [confirmedFiles](const GeneratableFile &qf) {
|
QtConcurrent::blockingFilter(queuedFiles, [confirmedFiles](const GeneratableFile &qf) {
|
||||||
@@ -92,6 +165,65 @@ void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString WARNING_MISSING_STRUCTURE_FATAL = QCoreApplication::tr(
|
||||||
|
"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::tr(
|
||||||
|
"The project is not properly structured for automatically generating CMake files.\n\nThe following files will be created:\n\n%1");
|
||||||
|
const QString WARNING_TITLE_FATAL = QCoreApplication::tr(
|
||||||
|
"Cannot Generate CMake Files");
|
||||||
|
const QString WARNING_TITLE_NONFATAL = QCoreApplication::tr(
|
||||||
|
"Problems with Generating CMake Files");
|
||||||
|
|
||||||
|
void showProjectDirErrorDialog(int error)
|
||||||
|
{
|
||||||
|
QString fatalList;
|
||||||
|
QString nonFatalList;
|
||||||
|
|
||||||
|
if (error & MISSING_CONTENTDIR)
|
||||||
|
fatalList.append(QString(DIRNAME_CONTENT) + "\n");
|
||||||
|
if (error & MISSING_APPMAINQML)
|
||||||
|
fatalList.append(QString(DIRNAME_CONTENT)
|
||||||
|
+ QDir::separator()
|
||||||
|
+ QString(FILENAME_APPMAINQML)
|
||||||
|
+ "\n");
|
||||||
|
if (error & MISSING_CPPDIR)
|
||||||
|
fatalList.append(QString(DIRNAME_CPP) + "\n");
|
||||||
|
if (error & MISSING_IMPORTDIR)
|
||||||
|
fatalList.append(QString(DIRNAME_IMPORT) + "\n");
|
||||||
|
|
||||||
|
if (error & MISSING_MAINCMAKE)
|
||||||
|
nonFatalList.append(QString(FILENAME_CMAKELISTS) + "\n");
|
||||||
|
if (error & MISSING_QMLMODULES)
|
||||||
|
nonFatalList.append(QString(FILENAME_MODULES) + "\n");
|
||||||
|
|
||||||
|
if (error & MISSING_MAINQML)
|
||||||
|
nonFatalList.append(QString(FILENAME_MAINQML) + "\n");
|
||||||
|
|
||||||
|
if (error & MISSING_MAINCPP)
|
||||||
|
nonFatalList.append(QString(DIRNAME_CPP)
|
||||||
|
+ QDir::separator()
|
||||||
|
+ QString(FILENAME_MAINCPP)
|
||||||
|
+ "\n");
|
||||||
|
if (error & MISSING_MAINCPP_HEADER)
|
||||||
|
nonFatalList.append(QString(DIRNAME_CPP)
|
||||||
|
+ QDir::separator()
|
||||||
|
+ QString(FILENAME_MAINCPP_HEADER)
|
||||||
|
+ "\n");
|
||||||
|
|
||||||
|
bool isFatal = isErrorFatal(error);
|
||||||
|
|
||||||
|
if (isFatal) {
|
||||||
|
QMessageBox::critical(nullptr,
|
||||||
|
WARNING_TITLE_FATAL,
|
||||||
|
WARNING_MISSING_STRUCTURE_FATAL.arg(fatalList + nonFatalList));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QMessageBox::warning(nullptr,
|
||||||
|
WARNING_TITLE_NONFATAL,
|
||||||
|
WARNING_MISSING_STRUCTURE_NONFATAL.arg(nonFatalList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool showConfirmationDialog(const Utils::FilePath &rootDir)
|
bool showConfirmationDialog(const Utils::FilePath &rootDir)
|
||||||
{
|
{
|
||||||
Utils::FilePaths files;
|
Utils::FilePaths files;
|
||||||
@@ -114,6 +246,7 @@ 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();
|
||||||
queuedFiles.append(file);
|
queuedFiles.append(file);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -159,9 +292,10 @@ 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;
|
||||||
|
|
||||||
const char CMAKEFILENAME[] = "CMakeLists.txt";
|
|
||||||
const char QMLDIRFILENAME[] = "qmldir";
|
const char QMLDIRFILENAME[] = "qmldir";
|
||||||
const char MODULEFILENAME[] = "qmlmodules";
|
|
||||||
|
const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl";
|
||||||
|
const char QMLMODULES_FILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodules.tpl";
|
||||||
|
|
||||||
bool generateCmakes(const FilePath &rootDir)
|
bool generateCmakes(const FilePath &rootDir)
|
||||||
{
|
{
|
||||||
@@ -177,9 +311,6 @@ bool generateCmakes(const FilePath &rootDir)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl";
|
|
||||||
const char QMLMODULES_FILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodules.tpl";
|
|
||||||
|
|
||||||
void generateMainCmake(const FilePath &rootDir)
|
void generateMainCmake(const FilePath &rootDir)
|
||||||
{
|
{
|
||||||
//TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all.
|
//TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all.
|
||||||
@@ -194,7 +325,7 @@ void generateMainCmake(const FilePath &rootDir)
|
|||||||
modulesAsPlugins.append(" " + moduleName + "plugin\n");
|
modulesAsPlugins.append(" " + moduleName + "plugin\n");
|
||||||
|
|
||||||
QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH).arg(appName).arg(modulesAsPlugins);
|
QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH).arg(appName).arg(modulesAsPlugins);
|
||||||
GenerateCmake::queueFile(rootDir.pathAppended(MODULEFILENAME), moduleFileContent);
|
GenerateCmake::queueFile(rootDir.pathAppended(GenerateCmake::FILENAME_MODULES), moduleFileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
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";
|
||||||
@@ -336,14 +467,14 @@ QStringList getDirectoryTreeResources(const FilePath &dir)
|
|||||||
|
|
||||||
void queueCmakeFile(const FilePath &dir, const QString &content)
|
void queueCmakeFile(const FilePath &dir, const QString &content)
|
||||||
{
|
{
|
||||||
FilePath filePath = dir.pathAppended(CMAKEFILENAME);
|
FilePath filePath = dir.pathAppended(GenerateCmake::FILENAME_CMAKELISTS);
|
||||||
GenerateCmake::queueFile(filePath, content);
|
GenerateCmake::queueFile(filePath, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFileBlacklisted(const QString &fileName)
|
bool isFileBlacklisted(const QString &fileName)
|
||||||
{
|
{
|
||||||
return (!fileName.compare(QMLDIRFILENAME) ||
|
return (!fileName.compare(QMLDIRFILENAME) ||
|
||||||
!fileName.compare(CMAKEFILENAME));
|
!fileName.compare(GenerateCmake::FILENAME_CMAKELISTS));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -358,18 +489,15 @@ bool generateEntryPointFiles(const FilePath &dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char MAIN_CPPFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl";
|
const char MAIN_CPPFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl";
|
||||||
const char MAIN_CPPFILE_DIR[] = "src";
|
|
||||||
const char MAIN_CPPFILE_NAME[] = "main.cpp";
|
|
||||||
const char MAIN_CPPFILE_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl";
|
const char MAIN_CPPFILE_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl";
|
||||||
const char MAIN_CPPFILE_HEADER_NAME[] = "import_qml_plugins.h";
|
|
||||||
const char MAIN_CPPFILE_HEADER_PLUGIN_LINE[] = "Q_IMPORT_QML_PLUGIN(%1)\n";
|
const char MAIN_CPPFILE_HEADER_PLUGIN_LINE[] = "Q_IMPORT_QML_PLUGIN(%1)\n";
|
||||||
|
|
||||||
bool generateMainCpp(const FilePath &dir)
|
bool generateMainCpp(const FilePath &dir)
|
||||||
{
|
{
|
||||||
FilePath srcDir = dir.pathAppended(MAIN_CPPFILE_DIR);
|
FilePath srcDir = dir.pathAppended(GenerateCmake::DIRNAME_CPP);
|
||||||
|
|
||||||
QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH);
|
QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH);
|
||||||
FilePath cppFilePath = srcDir.pathAppended(MAIN_CPPFILE_NAME);
|
FilePath cppFilePath = srcDir.pathAppended(GenerateCmake::FILENAME_MAINCPP);
|
||||||
bool cppOk = GenerateCmake::queueFile(cppFilePath, cppContent);
|
bool cppOk = GenerateCmake::queueFile(cppFilePath, cppContent);
|
||||||
|
|
||||||
QString modulesAsPlugins;
|
QString modulesAsPlugins;
|
||||||
@@ -379,19 +507,18 @@ bool generateMainCpp(const FilePath &dir)
|
|||||||
|
|
||||||
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(MAIN_CPPFILE_HEADER_NAME);
|
FilePath headerFilePath = srcDir.pathAppended(GenerateCmake::FILENAME_MAINCPP_HEADER);
|
||||||
bool headerOk = GenerateCmake::queueFile(headerFilePath, headerContent);
|
bool headerOk = GenerateCmake::queueFile(headerFilePath, headerContent);
|
||||||
|
|
||||||
return cppOk && headerOk;
|
return cppOk && headerOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char MAIN_QMLFILE_PATH[] = ":/boilerplatetemplates/qmlprojectmainqml.tpl";
|
const char MAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmainqml.tpl";
|
||||||
const char MAIN_QMLFILE_NAME[] = "main.qml";
|
|
||||||
|
|
||||||
bool generateMainQml(const FilePath &dir)
|
bool generateMainQml(const FilePath &dir)
|
||||||
{
|
{
|
||||||
QString content = GenerateCmake::readTemplate(MAIN_QMLFILE_PATH);
|
QString content = GenerateCmake::readTemplate(MAIN_QMLFILE_TEMPLATE_PATH);
|
||||||
FilePath filePath = dir.pathAppended(MAIN_QMLFILE_NAME);
|
FilePath filePath = dir.pathAppended(GenerateCmake::FILENAME_MAINQML);
|
||||||
return GenerateCmake::queueFile(filePath, content);
|
return GenerateCmake::queueFile(filePath, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,13 +34,17 @@ namespace GenerateCmake {
|
|||||||
struct GeneratableFile {
|
struct GeneratableFile {
|
||||||
Utils::FilePath filePath;
|
Utils::FilePath filePath;
|
||||||
QString content;
|
QString content;
|
||||||
|
bool fileExists;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool operator==(const GeneratableFile &left, const GeneratableFile &right);
|
bool operator==(const GeneratableFile &left, const GeneratableFile &right);
|
||||||
|
|
||||||
void generateMenuEntry();
|
void generateMenuEntry();
|
||||||
void onGenerateCmakeLists();
|
void onGenerateCmakeLists();
|
||||||
|
bool isErrorFatal(int error);
|
||||||
|
int isProjectCorrectlyFormed(const Utils::FilePath &rootDir);
|
||||||
void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles);
|
void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles);
|
||||||
|
void showProjectDirErrorDialog(int error);
|
||||||
bool showConfirmationDialog(const Utils::FilePath &rootDir);
|
bool showConfirmationDialog(const Utils::FilePath &rootDir);
|
||||||
bool queueFile(const Utils::FilePath &filePath, const QString &fileContent);
|
bool queueFile(const Utils::FilePath &filePath, const QString &fileContent);
|
||||||
bool writeFile(const GeneratableFile &file);
|
bool writeFile(const GeneratableFile &file);
|
||||||
|
|||||||
Reference in New Issue
Block a user