Skip to content

builder: espidf: fix flag/arg pairing across compileCommandFragments - #1756

Open
94xhn wants to merge 1 commit into
platformio:developfrom
94xhn:fix-cross-fragment-flag-arg-pairing
Open

builder: espidf: fix flag/arg pairing across compileCommandFragments#1756
94xhn wants to merge 1 commit into
platformio:developfrom
94xhn:fix-cross-fragment-flag-arg-pairing

Conversation

@94xhn

@94xhn 94xhn commented Jul 9, 2026

Copy link
Copy Markdown

Description

prepare_build_envs() paired a flag like -include or -isystem with its argument by grabbing the entire next compileCommandFragments entry's fragment string (build_flag + " " + compile_commands[i + 1]["fragment"]). That only works if the argument is the only thing in that next fragment. If CMake splits -include and its path into two fragments where the second fragment contains the path plus more flags — as reported in #1730 with esp_lvgl_adapter's target_compile_options(... PUBLIC -include "path") — the whole remainder of that fragment gets swallowed as one bogus argument, and unrelated flags after it are silently lost. The bare path also ends up added to LIBS as a stray positional argument instead of being paired with -include at all, matching the exact symptom described in #1730 (xtensa-esp-elf-gcc: fatal error: cannot specify '-o' with '-c' with multiple files).

This flattens all fragments into a single token stream first, so a flag needing an argument is paired with just the next token regardless of which fragment it originally came from. It also re-quotes that argument if it contains a space before handing the joined string to ParseFlags(), since ParseFlags() re-splits string arguments with shlex.split() and would otherwise break an unquoted path (e.g. under Program Files on Windows) apart again.

Fixes #1730

Verification

No unit tests cover this function, and reproducing the full esp_lvgl_adapter build locally would need the whole ESP-IDF toolchain, so I verified this directly against the actual SCons.Environment.ParseFlags() implementation this file calls (via tool-scons from a local PlatformIO install), reproducing the exact shape of the bug report:

# -include's path split across two compileCommandFragments,
# same shape as esp_lvgl_adapter's target_compile_options(...)
cmds = [
    {"fragment": "-DCONFIG_FOO=1 -include"},
    {"fragment": '"/home/user/esp/lvgl_adapter/config.h"'},
    {"fragment": "-Wall -O2"},
]

Before this fix: -include and its path are lost — CCFLAGS gets a bogus flag built from -include plus the entire raw text of the next fragment, and depending on shape either drops -Wall/-O2 or dumps the path into LIBS as a positional argument (this second part is the literal cause of the "cannot specify '-o' with '-c' with multiple files" error in #1730, since PlatformIO then passes that stray path to the compiler as if it were another source file).

After this fix: CCFLAGS correctly gets ("-include", <File config.h>), and -Wall/-O2 are preserved as separate flags — confirmed by calling the project's actual ParseFlags() on the resulting strings, not just by reasoning about it.

Also checked the space-in-path path specifically (-isystem "C:/Program Files/foo"), confirming it survives as one CCFLAGS entry rather than getting re-split by ParseFlags()'s internal shlex.split().

Generative AI

I used generative AI tools when creating this PR, but a human has checked the code and is responsible for the code and the description above.

prepare_build_envs() paired a flag like -include or -isystem with
its argument by grabbing the *entire next* compileCommandFragments
entry's fragment string, e.g. build_flag + " " + compile_commands[i
+ 1]["fragment"]. That only happens to work if the argument is the
only thing in that next fragment; if CMake split -include and its
path into two fragments where the second fragment contains the path
plus more flags (eg esp_lvgl_adapter's target_compile_options(...
PUBLIC -include "path")), the whole remainder of that fragment gets
swallowed as one bogus argument, and unrelated flags after it are
lost.

Flatten all fragments into a single token stream first, so a flag
needing an argument is paired with just the next token regardless of
which fragment it originally came from. Also re-quote that argument
if it contains a space before handing the joined string to
ParseFlags(), since ParseFlags() re-splits string arguments with
shlex.split() and would otherwise break an unquoted path apart again.

Fixes platformio#1730

Signed-off-by: yi chen <94xhn1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant