From 267bfbcc87d3a5c6eab2e533ed1116dff2e75a73 Mon Sep 17 00:00:00 2001 From: spaceplushy Date: Wed, 29 Oct 2025 09:59:40 -0700 Subject: [PATCH 1/3] Fix: Handle -U flags with spaces in build_flags Fixes #5237 When using '-U MACRO' (with space) in build_flags, SCons' ParseFlags() splits it into two separate items in CCFLAGS: ['-U', 'MACRO']. The previous code only captured '-U' and attempted undef[2:] which resulted in an empty string. This fix iterates through CCFLAGS with an index to detect standalone '-U' flags and pairs them with the following macro name. Both formats are now supported: - '-UMACRO' (no space) - already working - '-U MACRO' (with space) - now fixed The solution is similar to how SCons handles '-D' flags with spaces. --- platformio/builder/tools/piobuild.py | 52 +++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/platformio/builder/tools/piobuild.py b/platformio/builder/tools/piobuild.py index 8ef2522cf7..c3867e5908 100644 --- a/platformio/builder/tools/piobuild.py +++ b/platformio/builder/tools/piobuild.py @@ -240,17 +240,51 @@ def ProcessFlags(env, flags): # pylint: disable=too-many-branches # Cancel any previous definition of name, either built in or # provided with a -U option // Issue #191 - undefines = [ - u - for u in env.get("CCFLAGS", []) - if isinstance(u, string_types) and u.startswith("-U") - ] + ccflags = env.get("CCFLAGS", []) + undefines = [] + + # Handle both "-UMACRO" and "-U MACRO" formats + i = 0 + while i < len(ccflags): + item = ccflags[i] + if isinstance(item, string_types) and item.startswith("-U"): + if len(item) == 2: # Just "-U" by itself + # Next item should be the macro name + if i + 1 < len(ccflags): + macro_name = ccflags[i + 1] + undefines.append(("-U", macro_name)) + i += 2 # Skip both "-U" and the macro name + continue + else: # "-UMACRONAME" format + macro_name = item[2:] + undefines.append(item) + i += 1 + continue + i += 1 + if undefines: for undef in undefines: - env["CCFLAGS"].remove(undef) - if undef[2:] in env["CPPDEFINES"]: - env["CPPDEFINES"].remove(undef[2:]) - env.Append(_CPPDEFFLAGS=" %s" % " ".join(undefines)) + # Handle both formats + if isinstance(undef, tuple): + flag, macro_name = undef + env["CCFLAGS"].remove(flag) + env["CCFLAGS"].remove(macro_name) + else: + macro_name = undef[2:] + env["CCFLAGS"].remove(undef) + + # Remove from CPPDEFINES if it exists + if macro_name in env["CPPDEFINES"]: + env["CPPDEFINES"].remove(macro_name) + + # Build the -U flags string + undef_flags = [] + for undef in undefines: + if isinstance(undef, tuple): + undef_flags.append(f"{undef[0]} {undef[1]}") + else: + undef_flags.append(undef) + env.Append(_CPPDEFFLAGS=" %s" % " ".join(undef_flags)) def ProcessUnFlags(env, flags): From 1fc9424afd4435e93f8f309dcb06a6bd1a88a63e Mon Sep 17 00:00:00 2001 From: spaceplushy Date: Wed, 29 Oct 2025 10:17:53 -0700 Subject: [PATCH 2/3] Fix: Display actual test suite status instead of hardcoded 'PASSED' Fixes #5183 The print_suite_footer function was hardcoding 'PASSED' for any non-error test suite status. This caused SKIPPED test suites to incorrectly display as PASSED. Changed to use the actual test_suite.status.name with appropriate color coding via status.to_ansi_color(). Now correctly displays: - PASSED (green) when tests pass - FAILED (red) when tests fail - ERRORED (red) when tests error - SKIPPED (yellow) when tests are skipped This ensures the footer status matches the actual test execution result. --- platformio/test/cli.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/platformio/test/cli.py b/platformio/test/cli.py index 78a28490b5..4fac61ef8d 100644 --- a/platformio/test/cli.py +++ b/platformio/test/cli.py @@ -189,17 +189,14 @@ def print_suite_header(test_suite): def print_suite_footer(test_suite): is_error = test_suite.status in (TestStatus.FAILED, TestStatus.ERRORED) + status_color = "red" if is_error else test_suite.status.to_ansi_color() util.print_labeled_bar( "%s [%s] Took %.2f seconds" % ( click.style( "%s:%s" % (test_suite.env_name, test_suite.test_name), bold=True ), - ( - click.style(test_suite.status.name, fg="red", bold=True) - if is_error - else click.style("PASSED", fg="green", bold=True) - ), + click.style(test_suite.status.name, fg=status_color, bold=True), test_suite.duration, ), is_error=is_error, From edc694cc815674dbbb6c5b3b6e598e13832e0b66 Mon Sep 17 00:00:00 2001 From: spaceplushy Date: Wed, 29 Oct 2025 10:26:35 -0700 Subject: [PATCH 3/3] Fix: Prevent infinite loop from circular extends in config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #5096 Previously, the walk_options method did not check for circular dependencies when processing 'extends' directives. This caused infinite loops when a section extended itself (directly or indirectly through a chain). This fix adds cycle detection by checking if a section is already in extends_done or extends_queue before adding it to the queue. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- platformio/project/config.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platformio/project/config.py b/platformio/project/config.py index 82b76abf09..15cca1d361 100644 --- a/platformio/project/config.py +++ b/platformio/project/config.py @@ -180,9 +180,12 @@ def walk_options(self, root_section): for option in self._parser.options(section): yield (section, option) if self._parser.has_option(section, "extends"): - extends_queue.extend( - self.parse_multi_values(self._parser.get(section, "extends")) - ) + for ext_section in self.parse_multi_values( + self._parser.get(section, "extends") + ): + # Prevent circular dependencies + if ext_section not in extends_done and ext_section not in extends_queue: + extends_queue.append(ext_section) def options(self, section=None, env=None): result = []