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 <thomas.hartmann@qt.io>
Reviewed-by: Burak Hancerli <burak.hancerli@qt.io>
This commit is contained in:
Knud Dollereder
2023-02-23 10:34:53 +01:00
committed by Thomas Hartmann
parent 43bf597611
commit 3387c68ff2
5 changed files with 249 additions and 2 deletions

View File

@@ -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"
)

View File

@@ -3,6 +3,7 @@
#include "qmlbuildsystem.h"
#include "qmlprojectconstants.h"
#include "mcubuildstep.h"
#include <QtCore5Compat/qtextcodec.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -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);
});

View File

@@ -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 <coreplugin/messagebox.h>
#include <cmakeprojectmanager/cmakekitinformation.h>
#include "qtsupport/qtsupportconstants.h"
#include "mcusupport/mcusupportconstants.h"
#include "mcusupport/mculegacyconstants.h"
#include "utils/aspects.h"
#include "utils/filepath.h"
#include <QVersionNumber>
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<Utils::StringAspect>();
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<Utils::StringAspect>();
args->setSettingsKey(processArgumentsKey);
args->setDisplayStyle(Utils::StringAspect::LineEditDisplay);
args->setLabelText(tr("Arguments:"));
args->setValue(arguments.join(" "));
auto* outDir = addAspect<Utils::StringAspect>();
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

View File

@@ -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 <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildstep.h>
#include <utils/id.h>
#include <QTemporaryDir>
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

View File

@@ -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<QMessageBox> lastMessageBox;
QdsLandingPage *landingPage = nullptr;
QdsLandingPageWidget *landingPageWidget = nullptr;
MCUBuildStepFactory mcuBuildStepFactory;
};
QmlProjectPlugin::~QmlProjectPlugin()