// 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 "mcukitmanager.h" #include "mculegacyconstants.h" #include "mcusupportconstants.h" #include "mcusupporttr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Utils; namespace McuSupport::Internal { const Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep"; void DeployMcuProcessStep::showError(const QString &text) { ProjectExplorer::DeploymentTask task(ProjectExplorer::Task::Error, text); ProjectExplorer::TaskHub::addTask(task); } DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Id id) : AbstractProcessStep(bc, id) , m_tmpDir() { if (!buildSystem()) { showError(Tr::tr("Cannot find a valid build system.")); return; } if (!m_tmpDir.isValid()) { showError(Tr::tr("Cannot create a valid build directory.")); return; } ProjectExplorer::Kit *kit = MCUBuildStepFactory::findMostRecentQulKit(); if (!kit) return; const QString root = findKitInformation(kit, Internal::Legacy::Constants::QUL_CMAKE_VAR); const FilePath rootPath = FilePath::fromString(root); cmd.setSettingsKey("QmlProject.Mcu.ProcessStep.Command"); cmd.setExpectedKind(PathChooser::Command); cmd.setLabelText(Tr::tr("Command:")); cmd.setValue(rootPath.pathAppended("/bin/qmlprojectexporter")); const Id toolChainConstant = Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; arguments = {ProcessArgs::quoteArg(buildSystem()->projectFilePath().path()), "--platform", findKitInformation(kit, "QUL_PLATFORM"), "--toolchain", kit->value(toolChainConstant).toString()}; args.setSettingsKey("QmlProject.Mcu.ProcessStep.Arguments"); args.setDisplayStyle(StringAspect::LineEditDisplay); args.setLabelText(Tr::tr("Arguments:")); args.setValue(ProcessArgs::joinArgs(arguments)); updateIncludeDirArgs(); outDir.setSettingsKey("QmlProject.Mcu.ProcessStep.BuildDirectory"); outDir.setExpectedKind(PathChooser::Directory); outDir.setLabelText(Tr::tr("Build directory:")); outDir.setPlaceHolderText(m_tmpDir.path()); setCommandLineProvider([this] { QString directory = outDir().path(); if (directory.isEmpty()) directory = m_tmpDir.path(); CommandLine cmdLine(cmd()); cmdLine.addArgs(args(), CommandLine::Raw); cmdLine.addArg("--outdir"); cmdLine.addArg(directory); return cmdLine; }); connect(this, &BuildStep::addOutput, this, [](const QString &str, OutputFormat fmt) { if (fmt == OutputFormat::ErrorMessage) showError(str); }); } // Workaround for QDS-13763, when UL-10456 is completed this can be removed with the next LTS void DeployMcuProcessStep::updateIncludeDirArgs() { ProjectExplorer::Kit *kit = MCUBuildStepFactory::findMostRecentQulKit(); if (!kit) return; // Remove the old include dirs (if any) bool removeMode = false; arguments.erase( std::remove_if( arguments.begin(), arguments.end(), [&](const QString &s) { if (s == "--include-dirs") return removeMode = true; if (removeMode && s.startsWith("--")) return removeMode = false; return removeMode; }), arguments.end()); const Id importPathConstant = QtSupport::Constants::KIT_QML_IMPORT_PATH; const FilePath qulIncludeDir = FilePath::fromVariant(kit->value(importPathConstant)); QStringList includeDirs = {ProcessArgs::quoteArg(qulIncludeDir.path())}; QStringList subDirs = {"Controls", "ControlsTemplates", "Extras", "Layers", "Layouts", "Profiling", "SafeRenderer", "Shapes", "StudioComponents", "Timeline", "VirtualKeyboard"}; std::transform( subDirs.begin(), subDirs.end(), std::back_inserter(includeDirs), [&](const QString &subDir) { return ProcessArgs::quoteArg(qulIncludeDir.pathAppended(subDir).path()); }); arguments.append({"--include-dirs", includeDirs.join(",")}); args.setValue(ProcessArgs::joinArgs(arguments)); }; QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit *kit, const QString &key) { 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 {}; } ProjectExplorer::Kit *MCUBuildStepFactory::findMostRecentQulKit() { ProjectExplorer::Kit *mcuKit = nullptr; for (auto availableKit : ProjectExplorer::KitManager::kits()) { if (!availableKit) continue; auto qulVersion = McuKitManager::kitQulVersion(availableKit); if (qulVersion.isNull()) continue; if (!mcuKit) mcuKit = availableKit; if (qulVersion > McuKitManager::kitQulVersion(mcuKit)) mcuKit = availableKit; } return mcuKit; } void MCUBuildStepFactory::updateDeployStep(ProjectExplorer::BuildConfiguration *bc, bool enabled) { if (!bc) return; ProjectExplorer::DeployConfiguration *deployConfiguration = bc->activeDeployConfiguration(); // Return if the kit is currupted or is an MCU kit (unsupported in Design Studio) if (!deployConfiguration || (bc->kit() && bc->kit()->hasValue(Constants::KIT_MCUTARGET_KITVERSION_KEY))) { // This branch is called multiple times when selecting the run configuration // Show the message only once when the kit changes (avoid repitition) static ProjectExplorer::Kit *previousSelectedKit = nullptr; if (previousSelectedKit && previousSelectedKit == bc->kit()) return; previousSelectedKit = bc->kit(); //TODO use DeployMcuProcessStep::showError instead when the Issues panel // supports poping up on new entries QMessageBox::warning( Core::ICore::dialogParent(), Tr::tr("The Selected Kit Is Not Supported"), Tr::tr("You cannot use the selected kit to preview Qt for MCUs applications.")); return; } ProjectExplorer::BuildStepList *stepList = deployConfiguration->stepList(); ProjectExplorer::BuildStep *step = stepList->firstStepWithId(DeployMcuProcessStep::id); if (!step && enabled) { if (findMostRecentQulKit()) { stepList->appendStep(DeployMcuProcessStep::id); } else { DeployMcuProcessStep::showError(Tr::tr("Cannot find a valid Qt for MCUs kit.")); } } else { if (!step) return; auto mcuStep = qobject_cast(step); if (mcuStep) mcuStep->updateIncludeDirArgs(); step->setStepEnabled(enabled); } } MCUBuildStepFactory::MCUBuildStepFactory() { setDisplayName(Tr::tr("Qt for MCUs Deploy Step")); registerStep(DeployMcuProcessStep::id); } } // namespace McuSupport::Internal