From 3387c68ff2daa1ff6aa78f9536feb0554d4fd878 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Thu, 23 Feb 2023 10:34:53 +0100 Subject: [PATCH] Add MCU build step to the QmlBuildSystem If a qml project is marked as qtForMCU the buildstep will run the qmlprojectexporter on the currently active qmlproject when pressing the "Run" button. The output messages of the tool will then show up in the "Compile Output" panel. The build step itself can be seen when switching to "Projects" mode under "Deployment". Change-Id: I5ac31d5655e3b4b6137aaf541839776f144a09c4 Reviewed-by: Thomas Hartmann Reviewed-by: Burak Hancerli --- src/plugins/qmlprojectmanager/CMakeLists.txt | 3 +- .../buildsystem/qmlbuildsystem.cpp | 7 +- .../qmlprojectmanager/mcubuildstep.cpp | 197 ++++++++++++++++++ src/plugins/qmlprojectmanager/mcubuildstep.h | 42 ++++ .../qmlprojectmanager/qmlprojectplugin.cpp | 2 + 5 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/mcubuildstep.cpp create mode 100644 src/plugins/qmlprojectmanager/mcubuildstep.h diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 3b7209d05c7..9b6e7609176 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -1,7 +1,7 @@ add_qtc_plugin(QmlProjectManager CONDITION TARGET Qt5::QuickWidgets PLUGIN_CLASS QmlProjectPlugin - DEPENDS QmlJS Qt5::QuickWidgets Utils + DEPENDS QmlJS Qt5::QuickWidgets Utils McuSupport CMakeProjectManager PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase SOURCES qmlprojectgen/qmlprojectgenerator.cpp qmlprojectgen/qmlprojectgenerator.h @@ -21,6 +21,7 @@ add_qtc_plugin(QmlProjectManager qmlprojectplugin.cpp qmlprojectplugin.h qmlprojectrunconfiguration.cpp qmlprojectrunconfiguration.h buildsystem/qmlbuildsystem.cpp buildsystem/qmlbuildsystem.h + mcubuildstep.cpp mcubuildstep.h "${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc" ) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 869e5b89c74..0872f24ede5 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -3,6 +3,7 @@ #include "qmlbuildsystem.h" #include "qmlprojectconstants.h" +#include "mcubuildstep.h" #include #include @@ -49,7 +50,11 @@ QmlBuildSystem::QmlBuildSystem(Target *target) updateDeploymentData(); registerMenuButtons(); - connect(target->project(), &Project::activeTargetChanged, [this]() { refresh(RefreshOptions::NoFileRefresh); }); + connect(target->project(), &Project::activeTargetChanged, [this](Target *target) { + refresh(RefreshOptions::NoFileRefresh); + if (qtForMCUs()) + MCUBuildStepFactory::attachToTarget(target); + }); connect(target->project(), &Project::projectFileIsDirty, [this]() { refresh(RefreshOptions::Project); }); diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.cpp b/src/plugins/qmlprojectmanager/mcubuildstep.cpp new file mode 100644 index 00000000000..7ee5bc83939 --- /dev/null +++ b/src/plugins/qmlprojectmanager/mcubuildstep.cpp @@ -0,0 +1,197 @@ +// Copyright (C) 2023 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 "mcubuildstep.h" + +#include "projectexplorer/buildstep.h" +#include "projectexplorer/buildsystem.h" +#include "projectexplorer/buildsteplist.h" +#include "projectexplorer/deployconfiguration.h" +#include "projectexplorer/kit.h" +#include "projectexplorer/target.h" +#include "projectexplorer/kitmanager.h" + +#include +#include + +#include "qtsupport/qtsupportconstants.h" +#include "mcusupport/mcusupportconstants.h" +#include "mcusupport/mculegacyconstants.h" + +#include "utils/aspects.h" +#include "utils/filepath.h" + +#include + +namespace QmlProjectManager { + +const Utils::Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep"; +const QString DeployMcuProcessStep::processCommandKey = "QmlProject.Mcu.ProcessStep.Command"; +const QString DeployMcuProcessStep::processArgumentsKey = "QmlProject.Mcu.ProcessStep.Arguments"; +const QString DeployMcuProcessStep::processWorkingDirectoryKey = "QmlProject.Mcu.ProcessStep.BuildDirectory"; + +void DeployMcuProcessStep::showError(const QString& text) { + Core::AsynchronousMessageBox::critical(tr("Qt4MCU Deploy Step"), text); +} + +// TODO: +// - Grabbing *a* kit might not be the best todo. +// Would be better to specify a specific version of Qt4MCU in the qmlproject file. +// Currently we use the kit with the greatest version number. +// +// - Do not compare to *legacy* constants. +// Sounds like they will stop existing at some point. +// Also: Find Constant for QUL_PLATFORM + +DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id) + : AbstractProcessStep(bc, id) + , m_tmpDir() +{ + if (not buildSystem()) { + showError(QObject::tr("Failed to find valid build system")); + return; + } + + if (not m_tmpDir.isValid()) { + showError(QObject::tr("Failed to create valid build directory")); + return; + } + + auto fixPath = [](const QString& path) -> QString { + return "\"" + QDir::toNativeSeparators(path) + "\""; + }; + + ProjectExplorer::Kit* kit = MCUBuildStepFactory::findMostRecentQulKit(); + if (not kit) + return; + + QString root = findKitInformation(kit, McuSupport::Internal::Legacy::Constants::QUL_CMAKE_VAR); + + auto* cmd = addAspect(); + cmd->setSettingsKey(processCommandKey); + cmd->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); + cmd->setExpectedKind(Utils::PathChooser::Command); + cmd->setLabelText(tr("Command:")); + cmd->setValue(QDir::toNativeSeparators(root + "/bin/qmlprojectexporter")); + + const char* importPathConstant = QtSupport::Constants::KIT_QML_IMPORT_PATH; + QString projectDir = buildSystem()->projectDirectory().toString(); + QString qulIncludeDir = kit->value(importPathConstant).toString( ); + QStringList includeDirs { + fixPath(qulIncludeDir), + fixPath(qulIncludeDir + "/Timeline"), + fixPath(projectDir + "/imports") + }; + + const char* toolChainConstant = McuSupport::Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; + QStringList arguments = { + fixPath(buildSystem()->projectFilePath().toString()), + "--platform", findKitInformation(kit, "QUL_PLATFORM"), + "--toolchain", kit->value(toolChainConstant).toString( ), + "--include-dirs", includeDirs.join(","), + }; + + auto* args = addAspect(); + args->setSettingsKey(processArgumentsKey); + args->setDisplayStyle(Utils::StringAspect::LineEditDisplay); + args->setLabelText(tr("Arguments:")); + args->setValue(arguments.join(" ")); + + auto* outDir = addAspect(); + outDir->setSettingsKey(processWorkingDirectoryKey); + outDir->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); + outDir->setExpectedKind(Utils::PathChooser::Directory); + outDir->setLabelText(tr("Build directory:")); + outDir->setPlaceHolderText(fixPath(m_tmpDir.path())); + + setCommandLineProvider([this, cmd, args, outDir, fixPath]() -> Utils::CommandLine { + auto directory = outDir->value(); + if (directory.isEmpty()) + directory = fixPath(m_tmpDir.path()); + + QString outArg = " --outdir " + directory; + return {cmd->filePath(), args->value() + outArg, Utils::CommandLine::Raw}; + }); +} + +bool DeployMcuProcessStep::init() +{ + if (!AbstractProcessStep::init()) + return false; + return true; +} + +void DeployMcuProcessStep::doRun() +{ + AbstractProcessStep::doRun(); +} + +QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit* kit, const QString& key) +{ + // This is (kind of) stolen from mcukitmanager.cpp. Might make sense to unify. + using namespace CMakeProjectManager; + const auto config = CMakeConfigurationKitAspect::configuration(kit).toList(); + const auto keyName = key.toUtf8(); + for (const CMakeProjectManager::CMakeConfigItem &configItem : config) { + if (configItem.key == keyName) + return QString::fromUtf8(configItem.value); + } + return {}; +} + + +MCUBuildStepFactory::MCUBuildStepFactory() + : BuildStepFactory() +{ + setDisplayName("Qt4MCU Deploy Step"); + registerStep< DeployMcuProcessStep >(DeployMcuProcessStep::id); +} + +void MCUBuildStepFactory::attachToTarget(ProjectExplorer::Target *target) +{ + if (not target) + return; + + ProjectExplorer::DeployConfiguration* deployConfiguration = target->activeDeployConfiguration(); + ProjectExplorer::BuildStepList* stepList = deployConfiguration->stepList(); + if (stepList->contains(DeployMcuProcessStep::id)) + return; + + if (not findMostRecentQulKit()) { + DeployMcuProcessStep::showError(QObject::tr("Failed to find valid Qt4MCU kit")); + return; + } + + for (BuildStepFactory *factory : BuildStepFactory::allBuildStepFactories()) { + if (factory->stepId() == DeployMcuProcessStep::id) { + ProjectExplorer::BuildStep* deployConfig = factory->create(stepList); + stepList->appendStep(deployConfig); + } + } +} + +ProjectExplorer::Kit* MCUBuildStepFactory::findMostRecentQulKit() +{ + // Stolen from mcukitmanager.cpp + auto kitQulVersion = [](const ProjectExplorer::Kit *kit) -> QVersionNumber { + const char* sdkVersion = McuSupport::Internal::Constants::KIT_MCUTARGET_SDKVERSION_KEY; + return QVersionNumber::fromString(kit->value(sdkVersion).toString()); + }; + + ProjectExplorer::Kit* kit = nullptr; + for (auto k : ProjectExplorer::KitManager::kits()) + { + auto qulVersion = kitQulVersion(k); + if (qulVersion.isNull( )) + continue; + + if (not kit) + kit = k; + + if (qulVersion > kitQulVersion(kit)) + kit = k; + } + return kit; +} + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.h b/src/plugins/qmlprojectmanager/mcubuildstep.h new file mode 100644 index 00000000000..e1167e1917a --- /dev/null +++ b/src/plugins/qmlprojectmanager/mcubuildstep.h @@ -0,0 +1,42 @@ +// Copyright (C) 2023 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 "projectexplorer/kit.h" +#include "projectexplorer/project.h" +#include +#include +#include + +#include + +namespace QmlProjectManager { + +class DeployMcuProcessStep : public ProjectExplorer::AbstractProcessStep +{ +public: + static const Utils::Id id; + static void showError(const QString& text); + + DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id); + +private: + bool init() override; + void doRun() override; + QString findKitInformation(ProjectExplorer::Kit* kit, const QString& key); + + static const QString processCommandKey; + static const QString processArgumentsKey; + static const QString processWorkingDirectoryKey; + QTemporaryDir m_tmpDir; +}; + +class MCUBuildStepFactory : public ProjectExplorer::BuildStepFactory +{ +public: + MCUBuildStepFactory(); + static void attachToTarget(ProjectExplorer::Target *target); + static ProjectExplorer::Kit* findMostRecentQulKit( ); +}; + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 0a87ef9bc90..dd33508636c 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "mcubuildstep.h" #include "qdslandingpage.h" #include "qmlprojectplugin.h" #include "qmlproject.h" @@ -93,6 +94,7 @@ public: QPointer lastMessageBox; QdsLandingPage *landingPage = nullptr; QdsLandingPageWidget *landingPageWidget = nullptr; + MCUBuildStepFactory mcuBuildStepFactory; }; QmlProjectPlugin::~QmlProjectPlugin()