Merge remote-tracking branch 'origin/qds/dev' into HEAD

Change-Id: I9d13aa050b1c7fb0954e2b63d13da2922d8f7218
This commit is contained in:
Tim Jenssen
2024-03-14 16:13:25 +01:00
476 changed files with 21237 additions and 10987 deletions

View File

@@ -49,13 +49,14 @@ extend_qtc_plugin(QmlProjectManager
cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h
generatecmakelists.cpp generatecmakelists.h
generatecmakelistsconstants.h
cmakegenerator.cpp cmakegenerator.h
boilerplate.qrc
)
add_qtc_library(QmlProjectManagerLib OBJECT
EXCLUDE_FROM_INSTALL
DEPENDS
QmlJS Utils
QmlJS Utils ProjectExplorer
INCLUDES
${CMAKE_CURRENT_LIST_DIR}
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem

View File

@@ -143,6 +143,7 @@ QString jsonToQmlProject(const QJsonObject &rootObject)
appendString("mainFile", runConfig["mainFile"].toString());
appendString("mainUiFile", runConfig["mainUiFile"].toString());
appendString("targetDirectory", deploymentConfig["targetDirectory"].toString());
appendBool("enableCMakeGeneration", deploymentConfig["enableCMakeGeneration"].toBool());
appendBool("widgetApp", runConfig["widgetApp"].toBool());
appendStringArray("importPaths", rootObject["importPaths"].toVariant().toStringList());
appendBreak();
@@ -282,7 +283,8 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile)
|| propName.contains("mainuifile", Qt::CaseInsensitive)
|| propName.contains("forcefreetype", Qt::CaseInsensitive)) {
currentObj = &runConfigObject;
} else if (propName.contains("targetdirectory", Qt::CaseInsensitive)) {
} else if (propName.contains("targetdirectory", Qt::CaseInsensitive)
|| propName.contains("enableCMakeGeneration", Qt::CaseInsensitive)) {
currentObj = &deploymentObject;
} else if (propName.contains("qtformcus", Qt::CaseInsensitive)) {
qtForMCUs = value.toBool();

View File

@@ -422,4 +422,16 @@ void QmlProjectItem::insertAndUpdateProjectFile(const QString &key, const QJsonV
m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8());
}
bool QmlProjectItem::enableCMakeGeneration() const
{
return m_project["deployment"].toObject()["enableCMakeGeneration"].toBool();
}
void QmlProjectItem::setEnableCMakeGeneration(bool enable)
{
QJsonObject obj = m_project["deployment"].toObject();
obj["enableCMakeGeneration"] = enable;
insertAndUpdateProjectFile("deployment", obj);
}
} // namespace QmlProjectManager

View File

@@ -88,6 +88,9 @@ public:
QJsonObject project() const;
bool enableCMakeGeneration() const;
void setEnableCMakeGeneration(bool enable);
signals:
void qmlFilesChanged(const QSet<QString> &, const QSet<QString> &);

View File

@@ -32,6 +32,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
#include "projectexplorer/projectmanager.h"
#include "projectitem/qmlprojectitem.h"
#include "projectnode/qmlprojectnodes.h"
@@ -76,6 +77,7 @@ void updateMcuBuildStep(Target *target, bool mcuEnabled)
QmlBuildSystem::QmlBuildSystem(Target *target)
: BuildSystem(target)
, m_cmakeGen(new GenerateCmake::CMakeGenerator(this, this))
{
// refresh first - project information is used e.g. to decide the default RC's
refresh(RefreshOptions::Project);
@@ -85,10 +87,12 @@ QmlBuildSystem::QmlBuildSystem(Target *target)
connect(target->project(), &Project::activeTargetChanged, this, [this](Target *target) {
refresh(RefreshOptions::NoFileRefresh);
m_cmakeGen->initialize(qmlProject());
updateMcuBuildStep(target, qtForMCUs());
});
connect(target->project(), &Project::projectFileIsDirty, this, [this] {
refresh(RefreshOptions::Project);
m_cmakeGen->initialize(qmlProject());
updateMcuBuildStep(project()->activeTarget(), qtForMCUs());
});
@@ -219,6 +223,13 @@ void QmlBuildSystem::initProjectItem()
&QmlProjectItem::qmlFilesChanged,
this,
&QmlBuildSystem::refreshFiles);
connect(m_projectItem.get(),
&QmlProjectItem::qmlFilesChanged,
m_cmakeGen,
&GenerateCmake::CMakeGenerator::update);
m_cmakeGen->setEnabled(m_projectItem->enableCMakeGeneration());
}
void QmlBuildSystem::parseProjectFiles()
@@ -389,6 +400,16 @@ Utils::FilePath QmlBuildSystem::getStartupQmlFileWithFallback() const
return {};
}
QmlBuildSystem *QmlBuildSystem::getStartupBuildSystem()
{
auto project = ProjectExplorer::ProjectManager::startupProject();
if (project && project->activeTarget() && project->activeTarget()->buildSystem()) {
return qobject_cast<QmlProjectManager::QmlBuildSystem *>(
project->activeTarget()->buildSystem());
}
return nullptr;
}
Utils::FilePath QmlBuildSystem::mainFilePath() const
{
const QString fileName = mainFile();

View File

@@ -8,6 +8,8 @@
#include "../qmlprojectmanager_global.h"
#include <projectexplorer/buildsystem.h>
#include "qmlprojectmanager/cmakegen/cmakegenerator.h"
namespace QmlProjectManager {
class QmlProject;
@@ -102,6 +104,8 @@ public:
Utils::FilePath getStartupQmlFileWithFallback() const;
static QmlBuildSystem *getStartupBuildSystem();
signals:
void projectChanged();
@@ -120,6 +124,8 @@ private:
void registerMenuButtons();
void updateDeploymentData();
friend class FilesUpdateBlocker;
GenerateCmake::CMakeGenerator* m_cmakeGen;
};
} // namespace QmlProjectManager

View File

@@ -1,5 +1,8 @@
<RCC>
<qresource prefix="/boilerplatetemplates">
<file>gencmakeroot.tpl</file>
<file>gencmakemodule.tpl</file>
<file>gencmakeheadercomponents.tpl</file>
<file>qmlprojectmaincpp.tpl</file>
<file>qmlprojectmaincppheader.tpl</file>
<file>qmlprojectenvheader.tpl</file>

View File

@@ -0,0 +1,569 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cmakegenerator.h"
#include "generatecmakelistsconstants.h"
#include "projectexplorer/projectmanager.h"
#include "projectexplorer/projectnodes.h"
#include "qmlprojectmanager/qmlproject.h"
#include <QRegularExpression>
#include <set>
namespace QmlProjectManager {
namespace GenerateCmake {
const char TEMPLATE_CMAKELISTS_ROOT[] = ":/boilerplatetemplates/gencmakeroot.tpl";
const char TEMPLATE_CMAKELISTS_MODULE[] = ":/boilerplatetemplates/gencmakemodule.tpl";
const char TEMPLATE_SOURCE_MAIN[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl";
const char TEMPLATE_HEADER_IMPORT_COMPS[] = ":/boilerplatetemplates/gencmakeheadercomponents.tpl";
const char TEMPLATE_HEADER_IMPORT_PLUGINS[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl";
const char TEMPLATE_HEADER_ENVIRONMENT[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl";
const char DO_NOT_EDIT_FILE_COMMENT[]
= "### This file is automatically generated by Qt Design Studio.\n"
"### Do not change\n\n";
const char TEMPLATE_BIG_RESOURCES[] = R"(
qt6_add_resources(%1 %2
BIG_RESOURCES
PREFIX "%3"
VERSION 1.0
FILES %4
))";
const char TEMPLATE_LINK_LIBRARIES[] = R"(
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
%3
))";
CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent)
: QObject(parent)
, m_root(std::make_shared<Node>())
, m_buildSystem(bs)
{}
void CMakeGenerator::setEnabled(bool enabled)
{
m_enabled = enabled;
}
void CMakeGenerator::initialize(QmlProject *project)
{
if (!m_enabled)
return;
m_srcs.clear();
m_moduleNames.clear();
m_root = std::make_shared<Node>();
m_root->module = true;
m_root->uri = QString("Main");
m_root->name = QString("Main");
m_root->dir = project->rootProjectDirectory();
m_projectName = project->displayName();
ProjectExplorer::ProjectNode *rootProjectNode = project->rootProjectNode();
parseNodeTree(m_root, rootProjectNode);
parseSourceTree();
createCMakeFiles(m_root);
createEntryPoints(m_root);
}
void CMakeGenerator::update(const QSet<QString> &added, const QSet<QString> &removed)
{
if (!m_enabled)
return;
std::set<NodePtr> dirtyModules;
for (const QString &add : added) {
const Utils::FilePath path = Utils::FilePath::fromString(add);
if (auto node = findOrCreateNode(m_root, path)) {
insertFile(node, path);
if (auto module = findModuleFor(node))
dirtyModules.insert(module);
} else {
qDebug() << "CmakeGen: Failed to find Folder node " << path;
}
}
for (const QString &remove : removed) {
const Utils::FilePath path = Utils::FilePath::fromString(remove);
if (auto node = findNode(m_root, path)) {
removeFile(node, path);
if (auto module = findModuleFor(node))
dirtyModules.insert(module);
}
}
for (auto module : dirtyModules)
createModuleCMakeFile(module);
}
std::vector<Utils::FilePath> CMakeGenerator::files(const NodePtr &node,
const FileGetter &getter) const
{
std::vector<Utils::FilePath> out = getter(node);
for (const CMakeGenerator::NodePtr &child : node->subdirs) {
if (child->module)
continue;
auto childFiles = files(child, getter);
out.insert(out.end(), childFiles.begin(), childFiles.end());
}
return out;
}
std::vector<Utils::FilePath> CMakeGenerator::qmlFiles(const NodePtr &node) const
{
return files(node, [](const NodePtr &n) { return n->files; });
}
std::vector<Utils::FilePath> CMakeGenerator::singletons(const NodePtr &node) const
{
return files(node, [](const NodePtr &n) { return n->singletons; });
}
std::vector<Utils::FilePath> CMakeGenerator::resources(const NodePtr &node) const
{
return files(node, [](const NodePtr &n) { return n->resources; });
}
std::vector<Utils::FilePath> CMakeGenerator::sources(const NodePtr &node) const
{
return files(node, [](const NodePtr &n) { return n->sources; });
}
void CMakeGenerator::createCMakeFiles(const NodePtr &node) const
{
if (isRootNode(node))
createMainCMakeFile(node);
if (node->module || hasChildModule(node))
createModuleCMakeFile(node);
for (const NodePtr &n : node->subdirs)
createCMakeFiles(n);
}
void CMakeGenerator::createMainCMakeFile(const NodePtr &node) const
{
const QString appName = m_projectName + "App";
const QString qtcontrolsConfFile = makeEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF);
QString fileSection = "";
if (!qtcontrolsConfFile.isEmpty())
fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile);
const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_ROOT);
const QString fileContent = fileTemplate.arg(appName, m_srcs.join(" "), fileSection);
const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt");
writeFile(file, fileContent);
}
void CMakeGenerator::createModuleCMakeFile(const NodePtr &node) const
{
Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt");
if (!node->module && hasChildModule(node)) {
QString content(DO_NOT_EDIT_FILE_COMMENT);
content.append(makeSubdirectoriesBlock(node));
writeFile(writeToFile, content);
return;
}
QString templatePrefix;
templatePrefix.append(makeSubdirectoriesBlock(node));
templatePrefix.append(makeSingletonBlock(node));
auto [resources, bigResources] = makeResourcesBlocks(node);
QString moduleContent;
moduleContent.append(makeQmlFilesBlock(node));
moduleContent.append(resources);
QString templatePostfix;
templatePostfix.append(bigResources);
if (isRootNode(node)) {
writeToFile = node->dir.pathAppended("qmlModules");
QString pluginNames;
for (const QString &moduleName : m_moduleNames)
pluginNames.append(" " + moduleName + "plugin\n");
templatePostfix += QString::fromUtf8(TEMPLATE_LINK_LIBRARIES, -1).arg(pluginNames);
}
const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_MODULE);
const QString fileContent
= fileTemplate.arg(node->name, node->uri, templatePrefix, moduleContent, templatePostfix);
writeFile(writeToFile, fileContent);
}
void CMakeGenerator::createEntryPoints(const NodePtr &node) const
{
createMainCppFile(node);
}
void CMakeGenerator::createMainCppFile(const NodePtr &node) const
{
const Utils::FilePath srcDir = node->dir.pathAppended(Constants::DIRNAME_CPP);
if (!srcDir.exists()) {
srcDir.createDir();
const Utils::FilePath componentsHeaderPath = srcDir.pathAppended(
"import_qml_components_plugins.h");
const QString componentsHeaderContent = readTemplate(TEMPLATE_HEADER_IMPORT_COMPS);
writeFile(componentsHeaderPath, componentsHeaderContent);
const Utils::FilePath cppFilePath = srcDir.pathAppended("main.cpp");
const QString cppContent = readTemplate(TEMPLATE_SOURCE_MAIN);
writeFile(cppFilePath, cppContent);
const Utils::FilePath envHeaderPath = srcDir.pathAppended("app_environment.h");
if (m_buildSystem) {
QString environment;
const QString qtcontrolsConfFile = makeEnvironmentVariable(
Constants::ENV_VARIABLE_CONTROLCONF);
for (Utils::EnvironmentItem &envItem : m_buildSystem->environment()) {
QString key = envItem.name;
QString value = envItem.value;
if (value == qtcontrolsConfFile)
value.prepend(":/");
environment.append(QString(" qputenv(\"%1\", \"%2\");\n").arg(key).arg(value));
}
const QString envHeaderContent
= readTemplate(TEMPLATE_HEADER_ENVIRONMENT).arg(environment);
writeFile(envHeaderPath, envHeaderContent);
}
}
QString moduleContent;
for (const QString &module : m_moduleNames)
moduleContent.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin"));
const QString headerContent = readTemplate(TEMPLATE_HEADER_IMPORT_PLUGINS).arg(moduleContent);
const Utils::FilePath headerFilePath = srcDir.pathAppended("import_qml_plugins.h");
writeFile(headerFilePath, headerContent);
}
void CMakeGenerator::writeFile(const Utils::FilePath &path, const QString &content) const
{
QFile fileHandle(path.toString());
fileHandle.open(QIODevice::WriteOnly);
QTextStream stream(&fileHandle);
stream << content;
fileHandle.close();
}
QString CMakeGenerator::makeRelative(const NodePtr &node, const Utils::FilePath &path) const
{
const QString dir = node->dir.toString();
return "\"" + Utils::FilePath::calcRelativePath(path.toString(), dir) + "\"";
}
QString CMakeGenerator::makeEnvironmentVariable(const QString &key) const
{
QString value;
if (m_buildSystem) {
auto envItems = m_buildSystem->environment();
auto confEnv = std::find_if(envItems.begin(),
envItems.end(),
[key](Utils::EnvironmentItem &item) { return item.name == key; });
if (confEnv != envItems.end())
value = confEnv->value;
}
return value;
}
QString CMakeGenerator::makeSingletonBlock(const NodePtr &node) const
{
const QString setProperties(
"set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n");
QString str;
for (const Utils::FilePath &path : node->singletons)
str.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true"));
return str;
}
QString CMakeGenerator::makeSubdirectoriesBlock(const NodePtr &node) const
{
QString str;
for (const NodePtr &n : node->subdirs) {
if (n->module || hasChildModule(n))
str.append(QString("add_subdirectory(%1)\n").arg(n->dir.fileName()));
}
return str;
}
QString CMakeGenerator::makeQmlFilesBlock(const NodePtr &node) const
{
QString qmlFileContent;
for (const Utils::FilePath &path : qmlFiles(node))
qmlFileContent.append(QString(" %1\n").arg(makeRelative(node, path)));
if (isRootNode(node) && qmlFileContent.isEmpty())
qmlFileContent.append(QString(" %1\n").arg("\"main.qml\""));
QString str;
if (!qmlFileContent.isEmpty())
str.append(QString(" QML_FILES\n%1").arg(qmlFileContent));
return str;
}
std::tuple<QString, QString> CMakeGenerator::makeResourcesBlocks(const NodePtr &node) const
{
QString resourcesOut;
QString bigResourcesOut;
QString resourceFiles;
std::vector<QString> bigResources;
for (const Utils::FilePath &path : resources(node)) {
if (path.fileSize() > 5000000) {
bigResources.push_back(makeRelative(node, path));
continue;
}
resourceFiles.append(QString(" %1\n").arg(makeRelative(node, path)));
}
if (!resourceFiles.isEmpty())
resourcesOut.append(QString(" RESOURCES\n%1").arg(resourceFiles));
QString templatePostfix;
if (!bigResources.empty()) {
QString resourceContent;
for (const QString &res : bigResources)
resourceContent.append(QString("\n %1").arg(res));
const QString prefixPath = QString(node->uri).replace('.', '/');
const QString prefix = "/qt/qml/" + prefixPath;
const QString resourceName = node->name + "BigResource";
bigResourcesOut = QString::fromUtf8(TEMPLATE_BIG_RESOURCES, -1)
.arg(node->name, resourceName, prefix, resourceContent);
}
return {resourcesOut, bigResourcesOut};
}
QString CMakeGenerator::readTemplate(const QString &templatePath) const
{
QFile templatefile(templatePath);
templatefile.open(QIODevice::ReadOnly);
QTextStream stream(&templatefile);
QString content = stream.readAll();
templatefile.close();
return content;
}
void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const
{
node->module = true;
QFile f(filePath.toString());
f.open(QIODevice::ReadOnly);
QTextStream stream(&f);
Utils::FilePath dir = filePath.parentDir();
while (!stream.atEnd()) {
const QString line = stream.readLine();
const QStringList tokenizedLine = line.split(QRegularExpression("\\s+"));
const QString maybeFileName = tokenizedLine.last();
if (tokenizedLine.first().compare("module", Qt::CaseInsensitive) == 0) {
node->uri = tokenizedLine.last();
node->name = QString(node->uri).replace('.', '_');
} else if (maybeFileName.endsWith(".qml", Qt::CaseInsensitive)) {
Utils::FilePath tmp = dir.pathAppended(maybeFileName);
if (tokenizedLine.first() == "singleton")
node->singletons.push_back(tmp);
}
}
f.close();
}
CMakeGenerator::NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const
{
NodePtr current = node;
while (current->parent) {
if (current->module)
return current;
current = current->parent;
}
return nullptr;
}
CMakeGenerator::NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::FilePath &path) const
{
const Utils::FilePath parentDir = path.parentDir();
for (NodePtr &child : node->subdirs) {
if (child->dir == parentDir)
return child;
if (path.isChildOf(child->dir))
return findNode(child, path);
}
return nullptr;
}
CMakeGenerator::NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node,
const Utils::FilePath &path) const
{
if (auto found = findNode(node, path))
return found;
if (!path.isChildOf(node->dir))
return nullptr;
const Utils::FilePath parentDir = path.parentDir();
const Utils::FilePath relative = parentDir.relativeChildPath(node->dir);
const QChar separator = relative.pathComponentSeparator();
const QList<QStringView> components = relative.pathView().split(separator);
NodePtr last = node;
for (const auto &comp : components) {
NodePtr newNode = std::make_shared<Node>();
newNode->parent = last;
newNode->name = comp.toString();
newNode->dir = last->dir.pathAppended(comp.toString());
last->subdirs.push_back(newNode);
last = newNode;
}
return last;
}
void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) const
{
if (path.fileName() == "qmldir") {
readQmlDir(path, node);
} else if (path.suffix() == "qml" || path.suffix() == "ui.qml") {
node->files.push_back(path);
} else if (path.suffix() == "cpp") {
node->sources.push_back(path);
} else if (isResource(path)) {
node->resources.push_back(path);
}
}
void CMakeGenerator::removeFile(NodePtr &node, const Utils::FilePath &path) const
{
if (path.fileName() == "qmldir") {
node->module = false;
node->singletons.clear();
node->uri = "";
node->name = path.parentDir().fileName();
} else if (path.suffix() == "qml") {
auto iter = std::find(node->files.begin(), node->files.end(), path);
if (iter != node->files.end())
node->files.erase(iter);
} else if (isResource(path)) {
auto iter = std::find(node->resources.begin(), node->resources.end(), path);
if (iter != node->resources.end())
node->resources.erase(iter);
}
}
bool CMakeGenerator::isRootNode(const NodePtr &node) const
{
return node->name == "Main";
}
bool CMakeGenerator::hasChildModule(const NodePtr &node) const
{
for (const NodePtr &child : node->subdirs) {
if (child->module)
return true;
if (hasChildModule(child))
return true;
}
return false;
}
bool CMakeGenerator::isResource(const Utils::FilePath &path) const
{
static const QStringList suffixes = {
"json", "mesh", "dae", "qad", "hints", "png", "hdr", "ttf", "jpg", "JPG",
"js", "qsb", "frag", "frag.qsb", "vert", "vert.qsb", "svg", "ktx"};
return suffixes.contains(path.suffix());
}
void CMakeGenerator::printModules(const NodePtr &node) const
{
if (node->module)
qDebug() << "Module: " << node->name;
for (const auto &child : node->subdirs)
printModules(child);
}
void CMakeGenerator::printNodeTree(const NodePtr &generatorNode, size_t indent) const
{
auto addIndent = [](size_t level) -> QString {
QString str;
for (size_t i = 0; i < level; ++i)
str += " ";
return str;
};
qDebug() << addIndent(indent) << "GeneratorNode: " << generatorNode->name;
qDebug() << addIndent(indent) << "directory: " << generatorNode->dir;
qDebug() << addIndent(indent) << "files: " << generatorNode->files;
qDebug() << addIndent(indent) << "singletons: " << generatorNode->singletons;
qDebug() << addIndent(indent) << "resources: " << generatorNode->resources;
qDebug() << addIndent(indent) << "sources: " << generatorNode->sources;
for (const auto &child : generatorNode->subdirs)
printNodeTree(child, indent + 1);
}
void CMakeGenerator::parseNodeTree(NodePtr &generatorNode,
const ProjectExplorer::FolderNode *folderNode)
{
for (const auto *childNode : folderNode->nodes()) {
if (const auto *subFolderNode = childNode->asFolderNode()) {
CMakeGenerator::NodePtr childGeneratorNode = std::make_shared<Node>();
childGeneratorNode->parent = generatorNode;
childGeneratorNode->dir = subFolderNode->filePath();
childGeneratorNode->name = subFolderNode->displayName();
childGeneratorNode->uri = childGeneratorNode->name;
parseNodeTree(childGeneratorNode, subFolderNode);
generatorNode->subdirs.push_back(childGeneratorNode);
} else if (auto *fileNode = childNode->asFileNode()) {
insertFile(generatorNode, fileNode->filePath());
}
}
if (generatorNode->name == "content")
generatorNode->module = true;
if (generatorNode->module)
m_moduleNames.push_back(generatorNode->name);
}
void CMakeGenerator::parseSourceTree()
{
m_srcs.clear();
const QString srcDir = m_root->dir.pathAppended(Constants::DIRNAME_CPP).path();
QDirIterator it(srcDir, QStringList({"*.cpp"}), QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString relative = Utils::FilePath::calcRelativePath(it.next(), m_root->dir.path());
m_srcs.push_back(relative);
}
if (m_srcs.empty())
m_srcs.push_back("src/main.cpp");
}
} // namespace GenerateCmake
} // namespace QmlProjectManager

View File

@@ -0,0 +1,103 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "utils/filepath.h"
#include <QObject>
namespace ProjectExplorer {
class FolderNode;
}
namespace QmlProjectManager {
class QmlProject;
class QmlBuildSystem;
namespace GenerateCmake {
class CMakeGenerator : public QObject
{
Q_OBJECT
public:
CMakeGenerator(QmlBuildSystem *bs, QObject *parent = nullptr);
void setEnabled(bool enabled);
void initialize(QmlProject *project);
void update(const QSet<QString> &added, const QSet<QString> &removed);
private:
struct Node
{
std::shared_ptr<Node> parent = nullptr;
bool module = false;
QString uri;
QString name;
Utils::FilePath dir;
std::vector<std::shared_ptr<Node>> subdirs;
std::vector<Utils::FilePath> files;
std::vector<Utils::FilePath> singletons;
std::vector<Utils::FilePath> resources;
std::vector<Utils::FilePath> sources;
};
using NodePtr = std::shared_ptr<Node>;
using FileGetter = std::function<std::vector<Utils::FilePath>(const NodePtr &)>;
std::vector<Utils::FilePath> files(const NodePtr &node, const FileGetter &getter) const;
std::vector<Utils::FilePath> qmlFiles(const NodePtr &node) const;
std::vector<Utils::FilePath> singletons(const NodePtr &node) const;
std::vector<Utils::FilePath> resources(const NodePtr &node) const;
std::vector<Utils::FilePath> sources(const NodePtr &node) const;
void createCMakeFiles(const NodePtr &node) const;
void createMainCMakeFile(const NodePtr &node) const;
void createModuleCMakeFile(const NodePtr &node) const;
void createEntryPoints(const NodePtr &node) const;
void createMainCppFile(const NodePtr &node) const;
void writeFile(const Utils::FilePath &path, const QString &content) const;
QString makeRelative(const NodePtr &node, const Utils::FilePath &path) const;
QString makeEnvironmentVariable(const QString &key) const;
QString makeSingletonBlock(const NodePtr &node) const;
QString makeSubdirectoriesBlock(const NodePtr &node) const;
QString makeQmlFilesBlock(const NodePtr &node) const;
std::tuple<QString, QString> makeResourcesBlocks(const NodePtr &node) const;
QString readTemplate(const QString &templatePath) const;
void readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const;
NodePtr findModuleFor(const NodePtr &node) const;
NodePtr findNode(NodePtr &node, const Utils::FilePath &path) const;
NodePtr findOrCreateNode(NodePtr &node, const Utils::FilePath &path) const;
void insertFile(NodePtr &node, const Utils::FilePath &path) const;
void removeFile(NodePtr &node, const Utils::FilePath &path) const;
bool isRootNode(const NodePtr &node) const;
bool hasChildModule(const NodePtr &node) const;
bool isResource(const Utils::FilePath &path) const;
void printModules(const NodePtr &generatorNode) const;
void printNodeTree(const NodePtr &generatorNode, size_t indent = 0) const;
void parseNodeTree(NodePtr &generatorNode, const ProjectExplorer::FolderNode *folderNode);
void parseSourceTree();
bool m_enabled = false;
QString m_projectName = {};
NodePtr m_root = {};
QStringList m_srcs = {};
std::vector<QString> m_moduleNames = {};
QmlBuildSystem *m_buildSystem = nullptr;
};
} // namespace GenerateCmake
} // namespace QmlProjectManager

View File

@@ -44,9 +44,15 @@ void CmakeProjectConverter::generateMenuEntry(QObject *parent)
action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
QObject::connect(ProjectExplorer::ProjectManager::instance(),
&ProjectExplorer::ProjectManager::startupProjectChanged, [action]() {
action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
});
&ProjectExplorer::ProjectManager::startupProjectChanged,
[action]() {
auto currentBuildSystem = QmlBuildSystem::getStartupBuildSystem();
bool isMCU = currentBuildSystem ? currentBuildSystem->qtForMCUs() : false;
action->setEnabled(isMCU
&& isProjectConvertable(
ProjectExplorer::ProjectManager::startupProject()));
});
}
bool CmakeProjectConverter::isProjectConvertable(const ProjectExplorer::Project *project)

View File

@@ -0,0 +1,19 @@
/*
* This file is automatically generated by Qt Design Studio.
* Do not change.
*/
#include "qqmlextensionplugin.h"
#ifdef BUILD_QDS_COMPONENTS
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ComponentsPlugin)
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EffectsPlugin)
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ApplicationPlugin)
Q_IMPORT_QML_PLUGIN(FlowViewPlugin)
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_LogicHelperPlugin)
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_MultiTextPlugin)
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin)
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin)
#endif

View File

@@ -0,0 +1,14 @@
### This file is automatically generated by Qt Design Studio.
### Do not change
%3
qt_add_library(%1 STATIC)
qt6_add_qml_module(%1
URI "%2"
VERSION 1.0
RESOURCE_PREFIX "/qt/qml"
%4
)
%5

View File

@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.21.1)
option(LINK_INSIGHT "Link Qt Insight Tracker library" ON)
option(BUILD_QDS_COMPONENTS "Build design studio components" ON)
project(%1 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY}
CACHE STRING "Import paths for Qt Creator's code model"
FORCE
)
find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick)
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3)
qt_standard_project_setup()
endif()
qt_add_executable(${CMAKE_PROJECT_NAME} %2)
qt_add_resources(${CMAKE_PROJECT_NAME} "configuration"
PREFIX "/"
%3
)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Qml
)
if (BUILD_QDS_COMPONENTS)
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents OPTIONAL)
endif()
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
if (LINK_INSIGHT)
include(${CMAKE_CURRENT_SOURCE_DIR}/insight OPTIONAL)
endif ()
include(GNUInstallDirs)
install(TARGETS ${CMAKE_PROJECT_NAME}
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

View File

@@ -16,6 +16,7 @@
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/buildsystem/qmlbuildsystem.h>
#include <qmlprojectmanager/qmlmainfileaspect.h>
#include <qmlprojectmanager/qmlproject.h>
#include <qmlprojectmanager/qmlprojectconstants.h>
@@ -85,16 +86,6 @@ enum ProjectDirectoryError {
const QString MENU_ITEM_GENERATE = Tr::tr("Generate CMake Build Files...");
const QmlBuildSystem *getBuildSystem()
{
auto project = ProjectExplorer::ProjectManager::startupProject();
if (project && project->activeTarget() && project->activeTarget()->buildSystem()) {
return qobject_cast<QmlProjectManager::QmlBuildSystem *>(
project->activeTarget()->buildSystem());
}
return nullptr;
}
void generateMenuEntry(QObject *parent)
{
Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE);
@@ -118,7 +109,7 @@ void generateMenuEntry(QObject *parent)
QObject::connect(ProjectExplorer::ProjectManager::instance(),
&ProjectExplorer::ProjectManager::startupProjectChanged,
[action]() {
if (auto buildSystem = getBuildSystem())
if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem())
action->setEnabled(!buildSystem->qtForMCUs());
});
}
@@ -284,7 +275,7 @@ const QString projectEnvironmentVariable(const QString &key)
{
QString value = {};
if (auto buildSystem = getBuildSystem()) {
if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) {
auto envItems = buildSystem->environment();
auto confEnv = std::find_if(envItems.begin(), envItems.end(), [key](EnvironmentItem &item) {
return item.name == key;
@@ -636,7 +627,7 @@ bool CmakeFileGenerator::generateMainCpp(const FilePath &dir)
bool envHeaderOk = true;
QString environment;
if (auto buildSystem = getBuildSystem()) {
if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) {
for (EnvironmentItem &envItem : buildSystem->environment()) {
QString key = envItem.name;
QString value = envItem.value;