Fix: Prevent infinite loop from circular extends in config#5303
Fix: Prevent infinite loop from circular extends in config#5303SpacePlushy wants to merge 3 commits intoplatformio:developfrom
Conversation
Fixes platformio#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.
Fixes platformio#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.
Resolves platformio#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 <noreply@anthropic.com>
|
The PR contains a lot of changed files. Please push the only related file. |
mahdirajaee
left a comment
There was a problem hiding this comment.
The cycle detection fix in config.py is concise: before appending to extends_queue, it checks if ext_section not in extends_done and ext_section not in extends_queue. This prevents infinite loops from circular extends chains (e.g., env:a extends env:b, env:b extends env:a). The approach is correct — extends_done tracks already-processed sections and extends_queue tracks pending ones, so checking both guarantees no section is visited twice.
One edge case to consider: this silently skips the circular reference rather than raising an error or warning. A user with an unintentional cycle in their platformio.ini will get no feedback that their config isn't being resolved as expected — the circular section will simply be ignored. Adding a click.secho warning or a log message when a cycle is detected would help with debuggability. Deeply nested but non-circular configs should be unaffected since extends_done only blocks re-visits.
Similar to PR #5302, this diff includes changes to piobuild.py (the -U flag handling) and test/cli.py (the hardcoded PASSED fix) that appear to be from separate concerns. Keeping each PR scoped to a single fix makes review and potential reverts cleaner.
Summary
Fixes #5096 - Resolves infinite loop when a config section extends itself (directly or indirectly).
Problem
Previously, the
walk_optionsmethod inplatformio/project/config.pydid not check for circular dependencies when processingextendsdirectives. This caused infinite loops in cases like:or more complex chains:
Solution
Added cycle detection by checking if a section is already in
extends_doneorextends_queuebefore adding it to the processing queue. This prevents sections from being processed multiple times and breaks circular dependency chains.Changes
walk_optionsmethod inplatformio/project/config.py(lines 180-188)extends_queue.extend()to iterating and checking each section individuallyif ext_section not in extends_done and ext_section not in extends_queueTesting
Tested with both simple and complex circular reference cases:
[base] extends = base[common] extends = base+[base] extends = commonBoth cases now complete successfully without hanging, while still preserving valid config inheritance behavior.
🤖 Generated with Claude Code