From 79da2814adb0b70c935d52aab3236e36f2da8cda Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Fri, 10 Jul 2026 14:11:42 +0200 Subject: [PATCH 1/7] Add `device-settings` debugger node handling --- tools/projmgr/include/ProjMgrParser.h | 2 ++ tools/projmgr/include/ProjMgrWorker.h | 2 ++ tools/projmgr/include/ProjMgrYamlParser.h | 2 ++ tools/projmgr/schemas/common.schema.json | 12 ++++++++++-- tools/projmgr/src/ProjMgrCbuildRun.cpp | 8 ++++++++ tools/projmgr/src/ProjMgrWorker.cpp | 1 + tools/projmgr/src/ProjMgrYamlParser.cpp | 11 ++++++++++- .../TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml | 3 +++ .../test/data/TestRunDebug/run-debug.csolution.yml | 3 +++ 9 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tools/projmgr/include/ProjMgrParser.h b/tools/projmgr/include/ProjMgrParser.h index 9cf78a769..b270dcef4 100644 --- a/tools/projmgr/include/ProjMgrParser.h +++ b/tools/projmgr/include/ProjMgrParser.h @@ -173,6 +173,7 @@ struct CustomItem { * debug protocol (jtag or swd) * debug clock speed * debug configuration file + * device settings * start pname * telnet options * systemview options @@ -183,6 +184,7 @@ struct DebuggerItem { std::string protocol; std::string clock; std::string dbgconf; + std::map deviceSettings; std::string startPname; std::vector telnet; SystemViewItem systemView; diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index c7d299408..e292177f9 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -386,6 +386,7 @@ struct TemplateItem { * debug protocol (jtag or swd) * debug clock speed * debug configuration file + * device settings * start pname * list of gdbserver items * list of telnet options items @@ -397,6 +398,7 @@ struct DebuggerType { std::string protocol; std::optional clock; std::string dbgconf; + std::map deviceSettings; std::string startPname; std::vector gdbserver; std::map telnet; diff --git a/tools/projmgr/include/ProjMgrYamlParser.h b/tools/projmgr/include/ProjMgrYamlParser.h index 78e4156e2..13f25aa8e 100644 --- a/tools/projmgr/include/ProjMgrYamlParser.h +++ b/tools/projmgr/include/ProjMgrYamlParser.h @@ -98,6 +98,7 @@ static constexpr const char* YAML_DEBUG_TOPOLOGY = "debug-topology"; static constexpr const char* YAML_DEBUGGER = "debugger"; static constexpr const char* YAML_DEBUGPORTS = "debugports"; static constexpr const char* YAML_DBGCONF = "dbgconf"; +static constexpr const char* YAML_DEVICE_SETTINGS = "device-settings"; static constexpr const char* YAML_DEFAULT = "default"; static constexpr const char* YAML_DEFAULTS = "defaults"; static constexpr const char* YAML_DEFINE = "define"; @@ -370,6 +371,7 @@ class ProjMgrYamlParser { void ParseString(const YAML::Node& parent, const std::string& key, std::string& value); void ParseNumber(const YAML::Node& parent, const std::string& file, const std::string& key, std::string& value); void ParseInt(const YAML::Node& parent, const std::string& key, int& value); + void ParseMapOfStrings(const YAML::Node& parent, const std::string& key, std::map& value); void ParseVector(const YAML::Node& parent, const std::string& key, std::vector& value); void ParseVectorOfStringPairs(const YAML::Node& parent, const std::string& key, std::vector>& value); void ParseVectorOrString(const YAML::Node& parent, const std::string& key, std::vector& value); diff --git a/tools/projmgr/schemas/common.schema.json b/tools/projmgr/schemas/common.schema.json index 7b4b04817..eccc3e481 100644 --- a/tools/projmgr/schemas/common.schema.json +++ b/tools/projmgr/schemas/common.schema.json @@ -2292,11 +2292,18 @@ "gdbserver": { "$ref": "#/definitions/GdbServersType" }, "telnet": { "$ref": "#/definitions/TelnetOptionsType" }, "rtt": { "$ref": "#/definitions/RttOptionsType" }, - "systemview": { "$ref": "#/definitions/SystemViewOptionType" } + "systemview": { "$ref": "#/definitions/SystemViewOptionType" }, + "device-settings": { "$ref": "#/definitions/DeviceSettingsType" } }, "additionalProperties": true, "required": ["name"] }, + "DeviceSettingsType": { + "title": "device-settings:\nDocumentation: https://open-cmsis-pack.github.io/cmsis-toolbox/pyOCD-Debugger/#device-settings", + "description": "Debugger device settings.", + "type": "object", + "additionalProperties": { "type": [ "string", "number", "boolean", "null" ] } + }, "RttOptionsType": { "title": "rtt:\nDocumentation: https://open-cmsis-pack.github.io/cmsis-toolbox/pyOCD-Debugger/#rtt", "description": "Configuration for RTT features.", @@ -2586,7 +2593,8 @@ "start-pname": { "type": "string", "description": "Debugger connects at start to this processor." }, "telnet": { "$ref": "#/definitions/TelnetOptionsType" }, "rtt": { "$ref": "#/definitions/RttOptionsType" }, - "systemview": { "$ref": "#/definitions/SystemViewOptionType" } + "systemview": { "$ref": "#/definitions/SystemViewOptionType" }, + "device-settings": { "$ref": "#/definitions/DeviceSettingsType" } }, "additionalProperties": true, "required": ["name"] diff --git a/tools/projmgr/src/ProjMgrCbuildRun.cpp b/tools/projmgr/src/ProjMgrCbuildRun.cpp index b76dbc0fa..ef81a8c77 100644 --- a/tools/projmgr/src/ProjMgrCbuildRun.cpp +++ b/tools/projmgr/src/ProjMgrCbuildRun.cpp @@ -24,6 +24,7 @@ class ProjMgrCbuildRun : public ProjMgrCbuildBase { void SetFilesNode(YAML::Node node, const std::vector& outputs); void SetResourcesNode(YAML::Node node, const SystemResourcesType& systemResources); void SetDebuggerNode(YAML::Node node, const DebuggerType& debugger); + void SetDeviceSettingsNode(YAML::Node node, const std::map& deviceSettings); void SetDebugVarsNode(YAML::Node node, const DebugVarsType& debugVars); void SetDebugSequencesNode(YAML::Node node, const std::vector& algorithms); void SetDebugSequencesBlockNode(YAML::Node node, const std::vector& blocks); @@ -183,6 +184,7 @@ void ProjMgrCbuildRun::SetDebuggerNode(YAML::Node node, const DebuggerType& debu if (!debugger.dbgconf.empty()) { SetNodeValue(node[YAML_DBGCONF], FormatPath(debugger.dbgconf, m_directory)); } + SetDeviceSettingsNode(node[YAML_DEVICE_SETTINGS], debugger.deviceSettings); SetNodeValue(node[YAML_START_PNAME], debugger.startPname); SetGdbServerNode(node[YAML_GDBSERVER], debugger.gdbserver); SetTelnetNode(node[YAML_TELNET], debugger.telnet); @@ -191,6 +193,12 @@ void ProjMgrCbuildRun::SetDebuggerNode(YAML::Node node, const DebuggerType& debu } } +void ProjMgrCbuildRun::SetDeviceSettingsNode(YAML::Node node, const map& deviceSettings) { + for (const auto& [key, value] : deviceSettings) { + node[key] = value; + } +} + YAML::Node ProjMgrCbuildRun::GetCustomNode(const CustomItem& value) { YAML::Node node; if (!value.scalar.empty()) { diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index de0c0b1f1..df90e641f 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -2740,6 +2740,7 @@ bool ProjMgrWorker::ProcessDebuggers(ContextItem& context) { } } context.debugger.startPname = m_activeTargetSet.debugger.startPname; + context.debugger.deviceSettings = m_activeTargetSet.debugger.deviceSettings; for (auto telnet : m_activeTargetSet.debugger.telnet) { if (!telnet.file.empty()) { if (!ProcessSequenceRelative(context, telnet.file, context.csolution->directory, false)) { diff --git a/tools/projmgr/src/ProjMgrYamlParser.cpp b/tools/projmgr/src/ProjMgrYamlParser.cpp index 2a052053f..ad7cc8577 100644 --- a/tools/projmgr/src/ProjMgrYamlParser.cpp +++ b/tools/projmgr/src/ProjMgrYamlParser.cpp @@ -402,6 +402,14 @@ void ProjMgrYamlParser::ParseInt(const YAML::Node& parent, const string& key, in } } +void ProjMgrYamlParser::ParseMapOfStrings(const YAML::Node& parent, const string& key, map& value) { + if (parent[key].IsDefined() && parent[key].IsMap()) { + for (const auto& item : parent[key]) { + value[item.first.as()] = YAML::IsNullString(item.second.as()) ? "" : item.second.as(); + } + } +} + void ProjMgrYamlParser::ParseVector(const YAML::Node& parent, const string& key, vector& value) { if (parent[key].IsDefined() && parent[key].IsSequence()) { value = parent[key].as>(); @@ -637,10 +645,11 @@ void ProjMgrYamlParser::ParseDebugger(const YAML::Node& parent, const string& fi ParseString(debuggerNode, YAML_PROTOCOL, debugger.protocol); ParseNumber(debuggerNode, file, YAML_CLOCK, debugger.clock); ParsePortablePath(debuggerNode, file, YAML_DBGCONF, debugger.dbgconf); + ParseMapOfStrings(debuggerNode, YAML_DEVICE_SETTINGS, debugger.deviceSettings); ParseString(debuggerNode, YAML_START_PNAME, debugger.startPname); ParseTelnet(debuggerNode, file, debugger.telnet); ParseSystemView(debuggerNode, file, debugger.systemView); - ParseCustom(debuggerNode, { YAML_NAME, YAML_PROTOCOL, YAML_CLOCK, YAML_DBGCONF, YAML_START_PNAME, YAML_TELNET, YAML_SYSTEMVIEW }, debugger.custom); + ParseCustom(debuggerNode, { YAML_NAME, YAML_PROTOCOL, YAML_CLOCK, YAML_DBGCONF, YAML_DEVICE_SETTINGS, YAML_START_PNAME, YAML_TELNET, YAML_SYSTEMVIEW }, debugger.custom); } } diff --git a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml index 8be60bf37..724345122 100644 --- a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml +++ b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml @@ -63,6 +63,9 @@ cbuild-run: protocol: jtag clock: 20000000 dbgconf: ../../data/TestRunDebug/RTE/Device/RteTest1/RteTest1.dbgconf + device-settings: + reset-mode: hardware + trace-port: enabled gdbserver: - port: 3333 debug-vars: diff --git a/tools/projmgr/test/data/TestRunDebug/run-debug.csolution.yml b/tools/projmgr/test/data/TestRunDebug/run-debug.csolution.yml index 250e712b1..a25e9fa70 100644 --- a/tools/projmgr/test/data/TestRunDebug/run-debug.csolution.yml +++ b/tools/projmgr/test/data/TestRunDebug/run-debug.csolution.yml @@ -22,6 +22,9 @@ solution: protocol: jtag clock: 20000000 dbgconf: RTE/Device/RteTest1/RteTest1.dbgconf + device-settings: + reset-mode: hardware + trace-port: enabled images: - project-context: run-debug load: none From 808b8c01f6d380c4df18d2a7abeb96005a4f20d5 Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Fri, 10 Jul 2026 16:18:31 +0200 Subject: [PATCH 2/7] Populate `device settings` from `dbgconf` assignments --- tools/projmgr/include/ProjMgrUtils.h | 7 ++ tools/projmgr/src/ProjMgrCbuildRun.cpp | 2 +- tools/projmgr/src/ProjMgrRunDebug.cpp | 6 +- tools/projmgr/src/ProjMgrUtils.cpp | 73 +++++++++++++++++++ .../ref/custom+TestHW.cbuild-run.yml | 5 ++ .../ref/run-debug+TestHW.cbuild-run.yml | 4 +- .../ref/run-debug+TestHW3.cbuild-run.yml | 5 ++ .../test/src/ProjMgrUtilsUnitTests.cpp | 24 ++++++ 8 files changed, 122 insertions(+), 4 deletions(-) diff --git a/tools/projmgr/include/ProjMgrUtils.h b/tools/projmgr/include/ProjMgrUtils.h index 2b2c62eee..0eda5243c 100644 --- a/tools/projmgr/include/ProjMgrUtils.h +++ b/tools/projmgr/include/ProjMgrUtils.h @@ -284,6 +284,13 @@ class ProjMgrUtils { */ static const std::string FormatPath(const std::string& original, const std::string& directory, bool useAbsolutePaths = false); + /** + * @brief parse variable assignments from a dbgconf file + * @param dbgconf path to dbgconf file + * @return map of variable names to assigned values + */ + static std::map ParseDbgconfFile(const std::string& dbgconf); + /** * @brief adjust relative path fragments in a list of values * @param vec list of strings that may contain relative paths diff --git a/tools/projmgr/src/ProjMgrCbuildRun.cpp b/tools/projmgr/src/ProjMgrCbuildRun.cpp index ef81a8c77..0ff5c9c13 100644 --- a/tools/projmgr/src/ProjMgrCbuildRun.cpp +++ b/tools/projmgr/src/ProjMgrCbuildRun.cpp @@ -184,11 +184,11 @@ void ProjMgrCbuildRun::SetDebuggerNode(YAML::Node node, const DebuggerType& debu if (!debugger.dbgconf.empty()) { SetNodeValue(node[YAML_DBGCONF], FormatPath(debugger.dbgconf, m_directory)); } - SetDeviceSettingsNode(node[YAML_DEVICE_SETTINGS], debugger.deviceSettings); SetNodeValue(node[YAML_START_PNAME], debugger.startPname); SetGdbServerNode(node[YAML_GDBSERVER], debugger.gdbserver); SetTelnetNode(node[YAML_TELNET], debugger.telnet); SetSystemViewNode(node[YAML_SYSTEMVIEW], debugger.systemView); + SetDeviceSettingsNode(node[YAML_DEVICE_SETTINGS], debugger.deviceSettings); SetCustomNodes(node, debugger.custom); } } diff --git a/tools/projmgr/src/ProjMgrRunDebug.cpp b/tools/projmgr/src/ProjMgrRunDebug.cpp index bd08669e5..956ada1c9 100644 --- a/tools/projmgr/src/ProjMgrRunDebug.cpp +++ b/tools/projmgr/src/ProjMgrRunDebug.cpp @@ -411,6 +411,11 @@ void ProjMgrRunDebug::CollectDebuggerSettings(const ContextItem& context, const m_runDebug.debugger = defaultDebugger; } + // device-settings from dbgconf + if (m_runDebug.debugger.deviceSettings.empty() && !m_runDebug.debugger.dbgconf.empty()) { + m_runDebug.debugger.deviceSettings = ProjMgrUtils::ParseDbgconfFile(m_runDebug.debugger.dbgconf); + } + // primary processor: pname of first cproject if (m_runDebug.debugger.startPname.empty()) { m_runDebug.debugger.startPname = context.deviceItem.pname; @@ -451,7 +456,6 @@ void ProjMgrRunDebug::CollectDebuggerSettings(const ContextItem& context, const // merge custom options MergeCustomItems(context.debugger.custom, m_runDebug.debugger.custom); - } void ProjMgrRunDebug::CollectTelnetOptions(const ContextItem& context, DebugAdapterItem& adapter, diff --git a/tools/projmgr/src/ProjMgrUtils.cpp b/tools/projmgr/src/ProjMgrUtils.cpp index 67a3df26c..31e4e642a 100644 --- a/tools/projmgr/src/ProjMgrUtils.cpp +++ b/tools/projmgr/src/ProjMgrUtils.cpp @@ -13,6 +13,7 @@ #include "CrossPlatform.h" #include "CrossPlatformUtils.h" +#include #include #include @@ -22,6 +23,52 @@ static constexpr const char* HIGHER_OR_EQUAL_OPERATOR = ">="; static constexpr const char* TILDE_OPERATOR = "~"; static constexpr const char* CARET_OPERATOR = "^"; +static bool IsDbgconfVariableName(const string& value) { + if (value.empty() || (!isalpha(static_cast(value[0])) && value[0] != '_')) { + return false; + } + return all_of(value.begin() + 1, value.end(), [](unsigned char c) { return isalnum(c) || c == '_'; }); +} + +static string Trim(const string& value) { + const auto first = find_if_not(value.begin(), value.end(), [](unsigned char c) { return isspace(c); }); + const auto last = find_if_not(value.rbegin(), value.rend(), [](unsigned char c) { return isspace(c); }).base(); + return first < last ? string(first, last) : string(); +} + +static string StripCComments(const string& input) { + string output; + bool inLineComment = false; + bool inBlockComment = false; + for (size_t index = 0; index < input.length(); ++index) { + const char current = input[index]; + const char next = index + 1 < input.length() ? input[index + 1] : '\0'; + + if (inLineComment) { + if (current == '\n' || current == '\r') { + inLineComment = false; + output += current; + } + } else if (inBlockComment) { + if (current == '*' && next == '/') { + inBlockComment = false; + ++index; + } else if (current == '\n' || current == '\r') { + output += current; + } + } else if (current == '/' && next == '/') { + inLineComment = true; + ++index; + } else if (current == '/' && next == '*') { + inBlockComment = true; + ++index; + } else { + output += current; + } + } + return output; +} + RtePackage* ProjMgrUtils::ReadGpdscFile(const string& gpdsc, bool& valid) { fs::path path(gpdsc); error_code ec; @@ -439,6 +486,32 @@ const string ProjMgrUtils::FormatPath(const string& original, const string& dire return path; } +map ProjMgrUtils::ParseDbgconfFile(const string& dbgconf) { + string content; + map assignments; + if (!RteFsUtils::ReadFile(dbgconf, content)) { + return assignments; + } + + const string contentWithoutComments = StripCComments(content); + size_t statementStart = 0; + size_t statementEnd = contentWithoutComments.find(';'); + while (statementEnd != string::npos) { + const string statement = contentWithoutComments.substr(statementStart, statementEnd - statementStart); + const size_t assignment = statement.find('='); + if (assignment != string::npos) { + const string key = Trim(statement.substr(0, assignment)); + const string value = Trim(statement.substr(assignment + 1)); + if (IsDbgconfVariableName(key)) { + assignments[key] = value; + } + } + statementStart = statementEnd + 1; + statementEnd = contentWithoutComments.find(';', statementStart); + } + return assignments; +} + bool ProjMgrUtils::ContainsIncompatiblePack(const std::list& packs, const std::string& requirement) { bool incompatible = false; for (const auto& p : packs) { diff --git a/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml index 98777c466..52ed42451 100644 --- a/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml +++ b/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml @@ -41,6 +41,11 @@ cbuild-run: protocol: jtag clock: 40000000 dbgconf: ../../data/TestRunDebug/.cmsis/custom+TestHW.dbgconf + device-settings: + ReleaseM0OnConnect: 1 + ReleaseM0SubOnConnect: 1 + RoutingTPIU: 0x00000000 + VecResetWithPeriph: 1 custom-adapter-key: custom adapter value custom-key-overwrite: custom value overwrite custom-map: diff --git a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml index 724345122..81621b077 100644 --- a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml +++ b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml @@ -63,11 +63,11 @@ cbuild-run: protocol: jtag clock: 20000000 dbgconf: ../../data/TestRunDebug/RTE/Device/RteTest1/RteTest1.dbgconf + gdbserver: + - port: 3333 device-settings: reset-mode: hardware trace-port: enabled - gdbserver: - - port: 3333 debug-vars: vars: | __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY diff --git a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml index 81d44bab3..bce53e731 100644 --- a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml +++ b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml @@ -99,6 +99,11 @@ cbuild-run: pname: cm0_core1 - port: 3334 pname: cm0_core0 + device-settings: + ReleaseM0OnConnect: 1 + ReleaseM0SubOnConnect: 1 + RoutingTPIU: 0x00000000 + VecResetWithPeriph: 1 debug-vars: vars: | __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY diff --git a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp index 617d3eea2..91b596c89 100644 --- a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp @@ -534,6 +534,30 @@ TEST_F(ProjMgrUtilsUnitTests, FormatPath) { } } +TEST_F(ProjMgrUtilsUnitTests, ParseDbgconfFile) { + const string testDir = testoutput_folder + "/ParseDbgconfFile"; + const string dbgconf = testDir + "/test.dbgconf"; + RteFsUtils::CreateDirectories(testDir); + ASSERT_TRUE(RteFsUtils::CreateTextFile(dbgconf, + "// leading comment\n" + "ReleaseM0OnConnect = 1; // trailing comment\n" + "/* block comment with ignored = assignment; */\n" + "RoutingTPIU =\n" + " 0x00000000;\n" + "TracePins = (1 << 18) | 3;\n" + "VecResetWithPeriph /* inline block comment */ = 1;\n" + "MissingTerminator = 2\n")); + + const map expected = { + {"ReleaseM0OnConnect", "1"}, + {"RoutingTPIU", "0x00000000"}, + {"TracePins", "(1 << 18) | 3"}, + {"VecResetWithPeriph", "1"}, + }; + EXPECT_EQ(expected, ProjMgrUtils::ParseDbgconfFile(dbgconf)); + EXPECT_TRUE(ProjMgrUtils::ParseDbgconfFile(testDir + "/missing.dbgconf").empty()); +} + TEST_F(ProjMgrUtilsUnitTests, ContainsIncompatiblePack) { ProjMgrKernel::Get()->SetCmsisPackRoot(testcmsispack_folder); std::list pdscFiles; From f8ccabb6ccd62eaccf121681525a1b6d641ef872 Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Fri, 10 Jul 2026 17:52:10 +0200 Subject: [PATCH 3/7] Update the schema and test reference data --- tools/projmgr/include/ProjMgrUtils.h | 2 +- tools/projmgr/schemas/common.schema.json | 2 +- .../test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml | 5 +++++ .../ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml | 5 +++++ .../test/data/WestSupport/ref/solution+CM0.cbuild-run.yml | 5 +++++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tools/projmgr/include/ProjMgrUtils.h b/tools/projmgr/include/ProjMgrUtils.h index 0eda5243c..efb448af0 100644 --- a/tools/projmgr/include/ProjMgrUtils.h +++ b/tools/projmgr/include/ProjMgrUtils.h @@ -285,7 +285,7 @@ class ProjMgrUtils { static const std::string FormatPath(const std::string& original, const std::string& directory, bool useAbsolutePaths = false); /** - * @brief parse variable assignments from a dbgconf file + * @brief parse semicolon-terminated variable assignments from a dbgconf file * @param dbgconf path to dbgconf file * @return map of variable names to assigned values */ diff --git a/tools/projmgr/schemas/common.schema.json b/tools/projmgr/schemas/common.schema.json index eccc3e481..82c4f9261 100644 --- a/tools/projmgr/schemas/common.schema.json +++ b/tools/projmgr/schemas/common.schema.json @@ -2302,7 +2302,7 @@ "title": "device-settings:\nDocumentation: https://open-cmsis-pack.github.io/cmsis-toolbox/pyOCD-Debugger/#device-settings", "description": "Debugger device settings.", "type": "object", - "additionalProperties": { "type": [ "string", "number", "boolean", "null" ] } + "additionalProperties": { "type": [ "string", "number", "boolean" ] } }, "RttOptionsType": { "title": "rtt:\nDocumentation: https://open-cmsis-pack.github.io/cmsis-toolbox/pyOCD-Debugger/#rtt", diff --git a/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml b/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml index bc115cb46..832733ca8 100644 --- a/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml +++ b/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml @@ -47,6 +47,11 @@ cbuild-run: dbgconf: ../.cmsis/image-only+CM0.dbgconf gdbserver: - port: 3333 + device-settings: + ReleaseM0OnConnect: 1 + ReleaseM0SubOnConnect: 1 + RoutingTPIU: 0x00000000 + VecResetWithPeriph: 1 debug-vars: vars: | __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY diff --git a/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml b/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml index cb66e52b9..1404eca89 100644 --- a/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml +++ b/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml @@ -83,6 +83,11 @@ cbuild-run: pname: cm0_core0 - port: 3335 pname: cm0_core1 + device-settings: + ReleaseM0OnConnect: 1 + ReleaseM0SubOnConnect: 1 + RoutingTPIU: 0x00000000 + VecResetWithPeriph: 1 debug-vars: vars: | __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY diff --git a/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml b/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml index 8f3f0ef7f..037cd7a7a 100644 --- a/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml +++ b/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml @@ -99,6 +99,11 @@ cbuild-run: pname: cm0_core0 - port: 3334 pname: cm0_core1 + device-settings: + ReleaseM0OnConnect: 1 + ReleaseM0SubOnConnect: 1 + RoutingTPIU: 0x00000000 + VecResetWithPeriph: 1 debug-vars: vars: | __var DbgMCU_CR = 0x00000007; // DBGMCU_CR: DBG_SLEEP, DBG_STOP, DBG_STANDBY From 555d2c917667d5beeb81f54a9a56f70a85124301 Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Fri, 10 Jul 2026 18:24:36 +0200 Subject: [PATCH 4/7] Address codeQL findings --- tools/projmgr/src/ProjMgrUtils.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/projmgr/src/ProjMgrUtils.cpp b/tools/projmgr/src/ProjMgrUtils.cpp index 31e4e642a..68cc60a9c 100644 --- a/tools/projmgr/src/ProjMgrUtils.cpp +++ b/tools/projmgr/src/ProjMgrUtils.cpp @@ -40,7 +40,8 @@ static string StripCComments(const string& input) { string output; bool inLineComment = false; bool inBlockComment = false; - for (size_t index = 0; index < input.length(); ++index) { + size_t index = 0; + while (index < input.length()) { const char current = input[index]; const char next = index + 1 < input.length() ? input[index + 1] : '\0'; @@ -49,21 +50,26 @@ static string StripCComments(const string& input) { inLineComment = false; output += current; } + ++index; } else if (inBlockComment) { if (current == '*' && next == '/') { inBlockComment = false; + index += 2; + } else { + if (current == '\n' || current == '\r') { + output += current; + } ++index; - } else if (current == '\n' || current == '\r') { - output += current; } } else if (current == '/' && next == '/') { inLineComment = true; - ++index; + index += 2; } else if (current == '/' && next == '*') { inBlockComment = true; - ++index; + index += 2; } else { output += current; + ++index; } } return output; From 722b1e693e0e684b8646861a054a7830f7802568 Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Fri, 10 Jul 2026 19:01:54 +0200 Subject: [PATCH 5/7] cbuild-run: discard `dbgconf` when `device-settings` is populated --- tools/projmgr/src/ProjMgrRunDebug.cpp | 3 +++ .../test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml | 1 - .../data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml | 1 - .../test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml | 1 - .../test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml | 1 - .../data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml | 1 - .../test/data/WestSupport/ref/solution+CM0.cbuild-run.yml | 1 - 7 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tools/projmgr/src/ProjMgrRunDebug.cpp b/tools/projmgr/src/ProjMgrRunDebug.cpp index 956ada1c9..0581c0335 100644 --- a/tools/projmgr/src/ProjMgrRunDebug.cpp +++ b/tools/projmgr/src/ProjMgrRunDebug.cpp @@ -415,6 +415,9 @@ void ProjMgrRunDebug::CollectDebuggerSettings(const ContextItem& context, const if (m_runDebug.debugger.deviceSettings.empty() && !m_runDebug.debugger.dbgconf.empty()) { m_runDebug.debugger.deviceSettings = ProjMgrUtils::ParseDbgconfFile(m_runDebug.debugger.dbgconf); } + if (!m_runDebug.debugger.deviceSettings.empty()) { + m_runDebug.debugger.dbgconf.clear(); + } // primary processor: pname of first cproject if (m_runDebug.debugger.startPname.empty()) { diff --git a/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml b/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml index 832733ca8..3fd265a33 100644 --- a/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml +++ b/tools/projmgr/test/data/ImageOnly/ref/image-only+CM0.cbuild-run.yml @@ -44,7 +44,6 @@ cbuild-run: name: CMSIS-DAP@pyOCD protocol: swd clock: 10000000 - dbgconf: ../.cmsis/image-only+CM0.dbgconf gdbserver: - port: 3333 device-settings: diff --git a/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml b/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml index 1404eca89..afff6f78b 100644 --- a/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml +++ b/tools/projmgr/test/data/ImageOnly/ref/image-only-multicore+CM0.cbuild-run.yml @@ -76,7 +76,6 @@ cbuild-run: name: CMSIS-DAP@pyOCD protocol: swd clock: 10000000 - dbgconf: ../.cmsis/image-only-multicore+CM0.dbgconf gdbserver: - port: 3333 - port: 3334 diff --git a/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml index 52ed42451..202857136 100644 --- a/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml +++ b/tools/projmgr/test/data/TestRunDebug/ref/custom+TestHW.cbuild-run.yml @@ -40,7 +40,6 @@ cbuild-run: name: Test Custom Adapter protocol: jtag clock: 40000000 - dbgconf: ../../data/TestRunDebug/.cmsis/custom+TestHW.dbgconf device-settings: ReleaseM0OnConnect: 1 ReleaseM0SubOnConnect: 1 diff --git a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml index 81621b077..d0a472b62 100644 --- a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml +++ b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW.cbuild-run.yml @@ -62,7 +62,6 @@ cbuild-run: info: Info target-set protocol: jtag clock: 20000000 - dbgconf: ../../data/TestRunDebug/RTE/Device/RteTest1/RteTest1.dbgconf gdbserver: - port: 3333 device-settings: diff --git a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml index bce53e731..332ec86f1 100644 --- a/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml +++ b/tools/projmgr/test/data/TestRunDebug/ref/run-debug+TestHW3.cbuild-run.yml @@ -92,7 +92,6 @@ cbuild-run: name: CMSIS-DAP@pyOCD protocol: swd clock: 10000000 - dbgconf: ../../data/TestRunDebug/.cmsis/run-debug+TestHW3.dbgconf start-pname: cm0_core1 gdbserver: - port: 3333 diff --git a/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml b/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml index 037cd7a7a..ecf2e553e 100644 --- a/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml +++ b/tools/projmgr/test/data/WestSupport/ref/solution+CM0.cbuild-run.yml @@ -92,7 +92,6 @@ cbuild-run: name: CMSIS-DAP@pyOCD protocol: swd clock: 10000000 - dbgconf: ../.cmsis/solution+CM0.dbgconf start-pname: cm0_core0 gdbserver: - port: 3333 From 4f6a6aad5d2f27f9373b29bd161326b2b744aa0f Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Fri, 10 Jul 2026 20:00:36 +0200 Subject: [PATCH 6/7] Address review finding --- tools/projmgr/include/ProjMgrUtils.h | 2 +- tools/projmgr/src/ProjMgrUtils.cpp | 13 +++++++++++-- tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/projmgr/include/ProjMgrUtils.h b/tools/projmgr/include/ProjMgrUtils.h index efb448af0..988ee63d1 100644 --- a/tools/projmgr/include/ProjMgrUtils.h +++ b/tools/projmgr/include/ProjMgrUtils.h @@ -285,7 +285,7 @@ class ProjMgrUtils { static const std::string FormatPath(const std::string& original, const std::string& directory, bool useAbsolutePaths = false); /** - * @brief parse semicolon-terminated variable assignments from a dbgconf file + * @brief parse variable assignments from a dbgconf file * @param dbgconf path to dbgconf file * @return map of variable names to assigned values */ diff --git a/tools/projmgr/src/ProjMgrUtils.cpp b/tools/projmgr/src/ProjMgrUtils.cpp index 68cc60a9c..18c08d0ee 100644 --- a/tools/projmgr/src/ProjMgrUtils.cpp +++ b/tools/projmgr/src/ProjMgrUtils.cpp @@ -23,6 +23,7 @@ static constexpr const char* HIGHER_OR_EQUAL_OPERATOR = ">="; static constexpr const char* TILDE_OPERATOR = "~"; static constexpr const char* CARET_OPERATOR = "^"; +// Dbgconf assignment names follow C-style identifier rules. static bool IsDbgconfVariableName(const string& value) { if (value.empty() || (!isalpha(static_cast(value[0])) && value[0] != '_')) { return false; @@ -30,12 +31,14 @@ static bool IsDbgconfVariableName(const string& value) { return all_of(value.begin() + 1, value.end(), [](unsigned char c) { return isalnum(c) || c == '_'; }); } +// Remove surrounding whitespace without changing the assignment value content. static string Trim(const string& value) { const auto first = find_if_not(value.begin(), value.end(), [](unsigned char c) { return isspace(c); }); const auto last = find_if_not(value.rbegin(), value.rend(), [](unsigned char c) { return isspace(c); }).base(); return first < last ? string(first, last) : string(); } +// Strip C and C++ comments while preserving line breaks that separate statements. static string StripCComments(const string& input) { string output; bool inLineComment = false; @@ -500,10 +503,13 @@ map ProjMgrUtils::ParseDbgconfFile(const string& dbgconf) { } const string contentWithoutComments = StripCComments(content); + // Split semicolon-separated statements, also accepting a final unterminated assignment. size_t statementStart = 0; size_t statementEnd = contentWithoutComments.find(';'); - while (statementEnd != string::npos) { - const string statement = contentWithoutComments.substr(statementStart, statementEnd - statementStart); + while (statementStart < contentWithoutComments.length()) { + const string statement = contentWithoutComments.substr( + statementStart, + statementEnd == string::npos ? string::npos : statementEnd - statementStart); const size_t assignment = statement.find('='); if (assignment != string::npos) { const string key = Trim(statement.substr(0, assignment)); @@ -512,6 +518,9 @@ map ProjMgrUtils::ParseDbgconfFile(const string& dbgconf) { assignments[key] = value; } } + if (statementEnd == string::npos) { + break; + } statementStart = statementEnd + 1; statementEnd = contentWithoutComments.find(';', statementStart); } diff --git a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp index 91b596c89..72c34f5aa 100644 --- a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp @@ -553,6 +553,7 @@ TEST_F(ProjMgrUtilsUnitTests, ParseDbgconfFile) { {"RoutingTPIU", "0x00000000"}, {"TracePins", "(1 << 18) | 3"}, {"VecResetWithPeriph", "1"}, + {"MissingTerminator", "2"}, }; EXPECT_EQ(expected, ProjMgrUtils::ParseDbgconfFile(dbgconf)); EXPECT_TRUE(ProjMgrUtils::ParseDbgconfFile(testDir + "/missing.dbgconf").empty()); From 03b6dfb03dc32e47e36134d4765c6d50d31297b2 Mon Sep 17 00:00:00 2001 From: Daniel Brondani Date: Mon, 13 Jul 2026 12:14:23 +0200 Subject: [PATCH 7/7] Address review comment --- tools/projmgr/src/ProjMgrUtils.cpp | 13 ++++++++----- tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp | 7 +++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/tools/projmgr/src/ProjMgrUtils.cpp b/tools/projmgr/src/ProjMgrUtils.cpp index 18c08d0ee..702075ecc 100644 --- a/tools/projmgr/src/ProjMgrUtils.cpp +++ b/tools/projmgr/src/ProjMgrUtils.cpp @@ -42,7 +42,7 @@ static string Trim(const string& value) { static string StripCComments(const string& input) { string output; bool inLineComment = false; - bool inBlockComment = false; + size_t blockCommentDepth = 0; size_t index = 0; while (index < input.length()) { const char current = input[index]; @@ -54,9 +54,12 @@ static string StripCComments(const string& input) { output += current; } ++index; - } else if (inBlockComment) { - if (current == '*' && next == '/') { - inBlockComment = false; + } else if (blockCommentDepth > 0) { + if (current == '/' && next == '*') { + ++blockCommentDepth; + index += 2; + } else if (current == '*' && next == '/') { + --blockCommentDepth; index += 2; } else { if (current == '\n' || current == '\r') { @@ -68,7 +71,7 @@ static string StripCComments(const string& input) { inLineComment = true; index += 2; } else if (current == '/' && next == '*') { - inBlockComment = true; + blockCommentDepth = 1; index += 2; } else { output += current; diff --git a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp index 72c34f5aa..2e4855f5a 100644 --- a/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUtilsUnitTests.cpp @@ -546,6 +546,10 @@ TEST_F(ProjMgrUtilsUnitTests, ParseDbgconfFile) { " 0x00000000;\n" "TracePins = (1 << 18) | 3;\n" "VecResetWithPeriph /* inline block comment */ = 1;\n" + "NestedBlockComment = 3 /* outer /* inner */ still outer */;\n" + "LineCommentInBlock = 4 /* // not a line comment\n" + " still block comment */;\n" + "BlockCommentInLine = 5; // /* not a block comment */\n" "MissingTerminator = 2\n")); const map expected = { @@ -553,6 +557,9 @@ TEST_F(ProjMgrUtilsUnitTests, ParseDbgconfFile) { {"RoutingTPIU", "0x00000000"}, {"TracePins", "(1 << 18) | 3"}, {"VecResetWithPeriph", "1"}, + {"NestedBlockComment", "3"}, + {"LineCommentInBlock", "4"}, + {"BlockCommentInLine", "5"}, {"MissingTerminator", "2"}, }; EXPECT_EQ(expected, ProjMgrUtils::ParseDbgconfFile(dbgconf));