From bb71aa6d43fc3fd5b2642e476f91b12e3503a718 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 27 Sep 2022 15:28:12 +0200 Subject: [PATCH] QmlDesigner: Prepare split of puppet creator We can move the environment building outside of the puppet start. The puppet building is now broken and if we add it again it should be too done outside of process start. Change-Id: I33ea01428cf509c26409147d2784d0327478c3ed Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../designercore/instances/puppetstartdata.h | 21 ++ .../designercore/instances/puppetstarter.cpp | 96 +++++++ .../designercore/instances/puppetstarter.h | 93 +++++++ .../qmldesigner/puppetenvironmentbuilder.cpp | 241 ++++++++++++++++++ .../qmldesigner/puppetenvironmentbuilder.h | 54 ++++ src/plugins/qmldesigner/qmldesignercore.cmake | 3 + 7 files changed, 509 insertions(+) create mode 100644 src/plugins/qmldesigner/designercore/instances/puppetstartdata.h create mode 100644 src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp create mode 100644 src/plugins/qmldesigner/designercore/instances/puppetstarter.h create mode 100644 src/plugins/qmldesigner/puppetenvironmentbuilder.cpp create mode 100644 src/plugins/qmldesigner/puppetenvironmentbuilder.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b748886cfbe..d3da2d341c3 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -38,6 +38,7 @@ add_qtc_plugin(QmlDesigner dynamiclicensecheck.h generateresource.cpp generateresource.h openuiqmlfiledialog.cpp openuiqmlfiledialog.h openuiqmlfiledialog.ui + puppetenvironmentbuilder.cpp puppetenvironmentbuilder.h qmldesignerconstants.h qmldesignericons.h qmldesignerplugin.cpp qmldesignerplugin.h diff --git a/src/plugins/qmldesigner/designercore/instances/puppetstartdata.h b/src/plugins/qmldesigner/designercore/instances/puppetstartdata.h new file mode 100644 index 00000000000..1cd476759ed --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/puppetstartdata.h @@ -0,0 +1,21 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class PuppetStartData +{ +public: + QString puppetPath; + QString workingDirectoryPath; + QString forwardOutput; + QString freeTypeOption; + QString debugPuppet; + QProcessEnvironment environment; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp b/src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp new file mode 100644 index 00000000000..63b2ad53599 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp @@ -0,0 +1,96 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "puppetstarter.h" + +#include +#include +#include + +namespace QmlDesigner { + +PuppetStarter::PuppetStarter(ProjectExplorer::Target *target, const Model *model) + + : m_target(target) + , m_availablePuppetType(FallbackPuppet) + , m_model(model) +{ +} + +QProcessUniquePointer PuppetStarter::createPuppetProcess( + const PuppetStartData &data, + const QString &puppetMode, + const QString &socketToken, + std::function processOutputCallback, + std::function processFinishCallback, + const QStringList &customOptions) const +{ + return puppetProcess(data.puppetPath, + data.workingDirectoryPath, + data.forwardOutput, + data.freeTypeOption, + data.debugPuppet, + data.environment, + puppetMode, + socketToken, + processOutputCallback, + processFinishCallback, + customOptions); +} + +QProcessUniquePointer PuppetStarter::puppetProcess( + const QString &puppetPath, + const QString &workingDirectory, + const QString &forwardOutput, + const QString &freeTypeOption, + const QString &debugPuppet, + const QProcessEnvironment &processEnvironment, + const QString &puppetMode, + const QString &socketToken, + std::function processOutputCallback, + std::function processFinishCallback, + const QStringList &customOptions) const +{ + QProcessUniquePointer puppetProcess{new QProcess}; + puppetProcess->setObjectName(puppetMode); + puppetProcess->setProcessEnvironment(processEnvironment); + + QObject::connect(QCoreApplication::instance(), + &QCoreApplication::aboutToQuit, + puppetProcess.get(), + &QProcess::kill); + QObject::connect(puppetProcess.get(), + static_cast(&QProcess::finished), + processFinishCallback); + + if (forwardOutput == puppetMode || forwardOutput == "all") { + puppetProcess->setProcessChannelMode(QProcess::MergedChannels); + QObject::connect(puppetProcess.get(), &QProcess::readyRead, processOutputCallback); + } + puppetProcess->setWorkingDirectory(workingDirectory); + + QStringList processArguments; + if (puppetMode == "custom") + processArguments = customOptions; + else + processArguments = {socketToken, puppetMode}; + + processArguments.push_back("-graphicssystem raster"); + processArguments.push_back(freeTypeOption); + + puppetProcess->start(puppetPath, processArguments); + + if (debugPuppet == puppetMode || debugPuppet == "all") { + QMessageBox::information( + nullptr, + QCoreApplication::translate("PuppetStarter", "Puppet is starting..."), + QCoreApplication::translate( + "PuppetStarter", + "You can now attach your debugger to the %1 puppet with process id: %2.") + .arg(puppetMode, QString::number(puppetProcess->processId()))); + } + + return puppetProcess; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/puppetstarter.h b/src/plugins/qmldesigner/designercore/instances/puppetstarter.h new file mode 100644 index 00000000000..9bae37cdb6e --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/puppetstarter.h @@ -0,0 +1,93 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qprocessuniqueptr.h" + +#include + +#include + +#include +#include + +#include + +namespace ProjectExplorer { +class Target; +} // namespace ProjectExplorer + +namespace QmlDesigner { + +class PuppetBuildProgressDialog; +class Model; + +class PuppetStarter +{ +public: + enum PuppetType { FallbackPuppet, UserSpacePuppet, BinPathPuppet }; + + PuppetStarter(ProjectExplorer::Target *target, const Model *model); + + void createQml2PuppetExecutableIfMissing(); + + QProcessUniquePointer createPuppetProcess( + const PuppetStartData &data, + const QString &puppetMode, + const QString &socketToken, + std::function processOutputCallback, + std::function processFinishCallback, + const QStringList &customOptions = {}) const; + + void setQrcMappingString(const QString qrcMapping); + + static QString defaultPuppetToplevelBuildDirectory(); + static QString defaultPuppetFallbackDirectory(); + static QString qmlPuppetFallbackDirectory(const DesignerSettings &settings); + +protected: + bool startBuildProcess(const QString &buildDirectoryPath, + const QString &command, + const QStringList &processArguments = QStringList(), + PuppetBuildProgressDialog *progressDialog = nullptr) const; + static QString puppetSourceDirectoryPath(); + static QString qml2PuppetProjectFile(); + + bool checkPuppetIsReady(const QString &puppetPath) const; + bool qtIsSupported() const; + QProcessUniquePointer puppetProcess(const QString &puppetPath, + const QString &workingDirectory, + const QString &forwardOutput, + const QString &freeTypeOption, + const QString &debugPuppet, + const QProcessEnvironment &processEnvironment, + const QString &puppetMode, + const QString &socketToken, + std::function processOutputCallback, + std::function processFinishCallback, + const QStringList &customOptions) const; + + QProcessEnvironment processEnvironment() const; + + QString buildCommand() const; + QString qmakeCommand() const; + + QByteArray qtHash() const; + QDateTime qtLastModified() const; + QDateTime puppetSourceLastModified() const; + + bool useOnlyFallbackPuppet() const; + QString getStyleConfigFileName() const; + bool usesVirtualKeyboard() const; + +private: + mutable QString m_compileLog; + ProjectExplorer::Target *m_target = nullptr; + PuppetType m_availablePuppetType; + static QHash m_qml2PuppetForKitPuppetHash; + const Model *m_model = nullptr; + QString m_qrcMapping; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp new file mode 100644 index 00000000000..24e0469f048 --- /dev/null +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -0,0 +1,241 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "puppetenvironmentbuilder.h" +#include "designersettings.h" +#include "qmldesignerplugin.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +namespace { +Q_LOGGING_CATEGORY(puppetEnvirmentBuild, "qtc.puppet.environmentBuild", QtWarningMsg) + +void filterOutQtBaseImportPath(QStringList *stringList) +{ + Utils::erase(*stringList, [](const QString &string) { + QDir dir(string); + return dir.dirName() == "qml" + && !dir.entryInfoList(QStringList("QtTest"), QDir::Dirs).isEmpty(); + }); +} + +Utils::FilePath pathForBinPuppet(ProjectExplorer::Target *target) +{ + if (!target || !target->kit()) + return {}; + + QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); + + if (currentQtVersion) + return currentQtVersion->binPath().pathAppended("qml2puppet").withExecutableSuffix(); + + return {}; +} + +} // namespace + +QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const +{ + qCInfo(puppetEnvirmentBuild) << Q_FUNC_INFO; + m_availablePuppetType = determinePuppetType(); + m_environment = Utils::Environment::systemEnvironment(); + + addKit(); + addRendering(); + addControls(); + addPixelRatio(); + addVirtualKeyboard(); + addForceQApplication(); + addImportPaths(); + addCustomFileSelectors(); + + qCInfo(puppetEnvirmentBuild) << "Puppet environment:" << m_environment.toStringList(); + + return m_environment.toProcessEnvironment(); +} + +bool PuppetEnvironmentBuilder::usesVirtualKeyboard() const +{ + if (m_target) { + auto *qmlbuild = qobject_cast(m_target->buildSystem()); + + const Utils::EnvironmentItem virtualKeyboard("QT_IM_MODULE", "qtvirtualkeyboard"); + return qmlbuild && qmlbuild->environment().indexOf(virtualKeyboard); + } + + return false; +} + +QString PuppetEnvironmentBuilder::getStyleConfigFileName() const +{ + if (m_target) { + for (const Utils::FilePath &fileName : + m_target->project()->files(ProjectExplorer::Project::SourceFiles)) { + if (fileName.fileName() == "qtquickcontrols2.conf") + return fileName.toString(); + } + } + + return {}; +} + +void PuppetEnvironmentBuilder::addKit() const +{ + if (m_target) { + if (m_availablePuppetType == PuppetType::Kit) { + m_target->kit()->addToBuildEnvironment(m_environment); + const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(m_target->kit()); + if (qt) { // Kits without a Qt version should not have a puppet! + // Update PATH to include QT_HOST_BINS + m_environment.prependOrSetPath(qt->hostBinPath()); + } + } + } +} + +void PuppetEnvironmentBuilder::addRendering() const +{ + m_environment.set("QML_BAD_GUI_RENDER_LOOP", "true"); + m_environment.set("QML_PUPPET_MODE", "true"); + m_environment.set("QML_DISABLE_DISK_CACHE", "true"); + m_environment.set("QMLPUPPET_RENDER_EFFECTS", "true"); + if (!m_environment.hasKey("QT_SCREEN_SCALE_FACTORS") && !m_environment.hasKey("QT_SCALE_FACTOR")) + m_environment.set("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); + + const bool smoothRendering = m_designerSettings.value(DesignerSettingsKey::SMOOTH_RENDERING).toBool(); + + if (smoothRendering) + m_environment.set("QMLPUPPET_SMOOTH_RENDERING", "true"); +} + +void PuppetEnvironmentBuilder::addControls() const +{ + const QString controlsStyle = m_designerSettings.value(DesignerSettingsKey::CONTROLS_STYLE).toString(); + + if (!controlsStyle.isEmpty()) { + m_environment.set("QT_QUICK_CONTROLS_STYLE", controlsStyle); + m_environment.set("QT_LABS_CONTROLS_STYLE", controlsStyle); + } + + const QString styleConfigFileName = getStyleConfigFileName(); + + if (!styleConfigFileName.isEmpty()) + m_environment.appendOrSet("QT_QUICK_CONTROLS_CONF", styleConfigFileName); +} + +void PuppetEnvironmentBuilder::addPixelRatio() const +{ + m_environment.set("FORMEDITOR_DEVICE_PIXEL_RATIO", + QString::number(QmlDesignerPlugin::formEditorDevicePixelRatio())); +} + +void PuppetEnvironmentBuilder::addVirtualKeyboard() const +{ + if (usesVirtualKeyboard()) { + m_environment.set("QT_IM_MODULE", "qtvirtualkeyboard"); + m_environment.set("QT_VIRTUALKEYBOARD_DESKTOP_DISABLE", "1"); + } +} + +void PuppetEnvironmentBuilder::addQuick3D() const +{ + // set env var if QtQuick3D import exists + QmlDesigner::Import import = QmlDesigner::Import::createLibraryImport("QtQuick3D", "1.0"); + if (m_model.hasImport(import, true, true)) + m_environment.set("QMLDESIGNER_QUICK3D_MODE", "true"); + + import = QmlDesigner::Import::createLibraryImport("QtQuick3D.Particles3D", "1.0"); + if (m_model.hasImport(import, true, true)) + m_environment.set("QMLDESIGNER_QUICK3D_PARTICLES3D_MODE", "true"); + + bool particlemode = m_designerSettings.value("particleMode").toBool(); + if (!particlemode) + m_environment.set("QT_QUICK3D_DISABLE_PARTICLE_SYSTEMS", "1"); + else + m_environment.set("QT_QUICK3D_EDITOR_PARTICLE_SYSTEMS", "1"); +} + +void PuppetEnvironmentBuilder::addForceQApplication() const +{ + auto import = QmlDesigner::Import::createLibraryImport("QtCharts", "2.0"); + if (m_model.hasImport(import, true, true)) { + m_environment.set("QMLDESIGNER_FORCE_QAPPLICATION", "true"); + } else if (m_target) { + auto bs = qobject_cast(m_target->buildSystem()); + if (bs && bs->widgetApp()) + m_environment.set("QMLDESIGNER_FORCE_QAPPLICATION", "true"); + } +} + +void PuppetEnvironmentBuilder::addMultiLanguageDatatbase() const +{ + if (m_target) { + if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(m_target)) { + if (!multiLanguageAspect->databaseFilePath().isEmpty()) + m_environment.set("QT_MULTILANGUAGE_DATABASE", + multiLanguageAspect->databaseFilePath().toString()); + } + } +} + +void PuppetEnvironmentBuilder::addImportPaths() const +{ + QStringList importPaths = m_model.importPaths(); + + if (m_availablePuppetType == PuppetType::Fallback) + filterOutQtBaseImportPath(&importPaths); + + if (m_target) { + QStringList designerImports = m_target->additionalData("QmlDesignerImportPath").toStringList(); + importPaths.append(designerImports); + } + + if (m_availablePuppetType == PuppetType::Fallback) + importPaths.prepend(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); + + constexpr auto pathSep = Utils::HostOsInfo::pathListSeparator(); + m_environment.appendOrSet("QML2_IMPORT_PATH", importPaths.join(pathSep), pathSep); + + qCInfo(puppetEnvirmentBuild) << "Puppet import paths:" << importPaths; +} + +void PuppetEnvironmentBuilder::addCustomFileSelectors() const +{ + QStringList customFileSelectors; + + if (m_target) + customFileSelectors = m_target->additionalData("CustomFileSelectorsData").toStringList(); + + customFileSelectors.append("DesignMode"); + + constexpr auto pathSep = Utils::HostOsInfo::pathListSeparator(); + if (!customFileSelectors.isEmpty()) + m_environment.appendOrSet("QML_FILE_SELECTORS", customFileSelectors.join(","), pathSep); + + qCInfo(puppetEnvirmentBuild) << "Puppet selectors:" << customFileSelectors; +} + +PuppetType PuppetEnvironmentBuilder::determinePuppetType() const +{ + if (m_target && m_target->kit() && m_target->kit()->isValid()) { + if (pathForBinPuppet(m_target).isExecutableFile()) + return PuppetType::Kit; + } + + return PuppetType::Fallback; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.h b/src/plugins/qmldesigner/puppetenvironmentbuilder.h new file mode 100644 index 00000000000..09aab672a20 --- /dev/null +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.h @@ -0,0 +1,54 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include + +namespace ProjectExplorer { +class Target; +} + +namespace QmlDesigner { + +enum class PuppetType { Fallback, UserSpace, Kit }; + +class PuppetEnvironmentBuilder +{ +public: + PuppetEnvironmentBuilder(ProjectExplorer::Target *target, + const class DesignerSettings &designerSettings, + const class Model &model) + : m_target(target) + , m_designerSettings(designerSettings) + , m_model(model) + {} + + QProcessEnvironment processEnvironment() const; + +private: + PuppetType determinePuppetType() const; + bool usesVirtualKeyboard() const; + QString getStyleConfigFileName() const; + void addKit() const; + void addRendering() const; + void addControls() const; + void addPixelRatio() const; + void addVirtualKeyboard() const; + void addQuick3D() const; + void addForceQApplication() const; + void addMultiLanguageDatatbase() const; + void addImportPaths() const; + void addCustomFileSelectors() const; + +private: + ProjectExplorer::Target *m_target = nullptr; + const DesignerSettings &m_designerSettings; + const Model &m_model; + mutable PuppetType m_availablePuppetType = {}; + mutable Utils::Environment m_environment; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignercore.cmake b/src/plugins/qmldesigner/qmldesignercore.cmake index c2dd2d6d5a3..7863ba30726 100644 --- a/src/plugins/qmldesigner/qmldesignercore.cmake +++ b/src/plugins/qmldesigner/qmldesignercore.cmake @@ -225,6 +225,9 @@ function(extend_with_qmldesigner_core target_name) instances/puppetbuildprogressdialog.h instances/puppetcreator.cpp instances/puppetcreator.h + instances/puppetstarter.cpp + instances/puppetstarter.h + instances/puppetstartdata.h instances/puppetdialog.cpp instances/puppetdialog.h instances/qprocessuniqueptr.h