Skip to content

Fix: include file contents in custom_sdkconfig hash computation#441

Closed
antoinecellerier wants to merge 1 commit into
pioarduino:developfrom
antoinecellerier:fix/stale-sdkconfig-cleanup
Closed

Fix: include file contents in custom_sdkconfig hash computation#441
antoinecellerier wants to merge 1 commit into
pioarduino:developfrom
antoinecellerier:fix/stale-sdkconfig-cleanup

Conversation

@antoinecellerier
Copy link
Copy Markdown

@antoinecellerier antoinecellerier commented Mar 24, 2026

Problem

When custom_sdkconfig uses a file:// reference:

custom_sdkconfig = file://sdkconfig.custom

the hash used to detect configuration changes is computed from the literal text "file://sdkconfig.custom" rather than the file's actual contents. Editing the referenced file doesn't hange the hash, so matching_custom_sdkconfig() returns True and stale recompiled libs are silently reused — even if the file now disables a major feature like Bluetooth.

This is the only way to pass Kconfig disable syntax (# CONFIG_BT_ENABLED is not set) since # lines in platformio.ini are stripped by the INI parser.

Fix

Resolve file:// references before hash computation in both arduino.py (where the hash is checked via matching_custom_sdkconfig()) and espidf.py (where it is written via write_sdkconfig_file()). Both sides now hash the resolved file contents + inline options + MCU name, so editing the file triggers lib recompilation via the existing Reinstall path (which also runs safe_remove_sdkconfig_files() to clean stale per-env sdkconfigs).

Inline custom_sdkconfig values (without file://) are unaffected.

Also guards PathCache.sdk_dir against framework_lib_dir being None (crashes with TypeError on Python 3.13 when framework packages are not yet installed). I'll admit I'm not sure why this kept happening in testing.

Repro

platformio.ini:

[env:test]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino
board = dfrobot_firebeetle2_esp32e
custom_sdkconfig = file://sdkconfig.custom
custom_component_remove =
    espressif/esp_insights
    espressif/esp_rainmaker
    espressif/rmaker_common
    espressif/esp_diag_data_store
    espressif/esp_diagnostics

sdkconfig.custom (initial):

CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=4
  rm -rf .pio sdkconfig.*
  pio run -e test          # Build 1: compiles libs, hash=3d58653b

Now edit sdkconfig.custom to add # CONFIG_BT_ENABLED is not set:

  pio run -e test          # Build 2: NO recompile (7s cached), hash unchanged
                           # BT still reserved (0xdb5c = 55KB) — file change silently ignored

With this fix, Build 2 detects the hash mismatch, triggers Reinstall + recompile, and properly disables BT (BTDM_RESERVE_DRAM drops from 0xdb5c to 0).

Use of AI

This PR was generated with the assistance of Claude Code (claude-opus-4-6). Do let me know if it still looks completely wrong.

Summary by CodeRabbit

Bug Fixes

  • Custom sdkconfig files referenced in project configuration now properly affect build cache validation, ensuring changes to these files trigger necessary rebuilds of compiled libraries.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

The changes enhance custom sdkconfig configuration handling by resolving file://... references within the custom_sdkconfig setting and incorporating the actual file contents into hash computations for sdkconfig state matching, ensuring that edits to referenced files trigger recompilation of stale libraries.

Changes

Cohort / File(s) Summary
Custom sdkconfig file resolution
builder/frameworks/arduino.py, builder/frameworks/espidf.py
Both files updated to resolve file://... references in custom_sdkconfig by reading the referenced file's contents and including them in hash computation for sdkconfig checksums. arduino.py also prevents PathCache.sdk_dir from constructing paths when framework_lib_dir is unavailable.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 The sdkconfig doth transform,
File references now perform,
Hash the contents, not the path,
Stale libraries feel our wrath! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: fixing hash computation to include file contents from custom_sdkconfig references, which is the core issue addressed in both modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Jason2866
Copy link
Copy Markdown

Jason2866 commented Mar 24, 2026

Not correct. The file stores a hash to detect changes in setup. Deleting forces a compile of the Arduino libs. In short the AI does not get how HybridCompile is working

The file could be deleted if detected there is no custom_sdkconfig set at all. Not needed since it gets overwritten when outdated.

If this is a result of your WiP Arduino ULP HybridCompile. This part is completely wrong there.

@antoinecellerier
Copy link
Copy Markdown
Author

Interesting. Could this then be a case of the hash not properly accounting for configuration impacting dependencies? If so I can try looking in that direction. Apologies if the questions are a bit naive.

To your question regarding ULP, this should be independent but will re-validate thoroughly to ensure it really is. I was hitting data+bss limits trying to enable Wifi DPP on the project ... so ended-up needing to disable other components like Bluetooth. DPP ended up being a dead end but I was thinking solving being able to disable Bluetooth would be useful to others too.

When custom_sdkconfig uses a file:// reference (e.g.
custom_sdkconfig = file://sdkconfig.custom), the hash used to detect
configuration changes is computed from the option text
("file://sdkconfig.custom") rather than the file's actual contents.
Editing the referenced file doesn't change the hash, so
matching_custom_sdkconfig() returns True and stale recompiled libs
are silently reused — even if the file now disables a major feature
like Bluetooth.

Fix: resolve file:// references before hash computation in both
arduino.py (where the hash is checked) and espidf.py (where it is
written). Both sides now hash the file contents + inline options +
MCU name, so editing the file triggers lib recompilation via the
existing Reinstall path (which also runs safe_remove_sdkconfig_files
to clean stale per-env sdkconfigs).

Inline custom_sdkconfig values (without file://) are unaffected.

Tested on dfrobot_firebeetle2_esp32e with file://sdkconfig.custom:

  Before fix (stock pioarduino):
    Build 1: custom_sdkconfig = file://sdkconfig.custom
             (CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=4)
             → lib recompile, hash=3d58653b, RAM=45104,
               BTDM_RESERVE_DRAM=0xdb5c (55KB BT reservation)
    Edit:    add "# CONFIG_BT_ENABLED is not set" to file
    Build 2: NO recompile (7s cached), hash unchanged,
             BT still reserved — file change silently ignored

  After fix:
    Build 1: same → lib recompile, hash=5c9e7d85, RAM=45064
    Edit:    add "# CONFIG_BT_ENABLED is not set" to file
    Build 2: hash mismatch detected → Reinstall + recompile,
             "Replace: CONFIG_BT_ENABLED=y with: # not set",
             BTDM_RESERVE_DRAM=0, RAM=44160 (-944 bytes)

Also guards PathCache.sdk_dir against framework_lib_dir being None
(crashes with TypeError on Python 3.13 when packages are missing).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@antoinecellerier antoinecellerier force-pushed the fix/stale-sdkconfig-cleanup branch from a1578f1 to bbc82a1 Compare March 24, 2026 14:56
@antoinecellerier antoinecellerier changed the title Delete stale per-env sdkconfig during lib recompile cleanup Fix: include file contents in custom_sdkconfig hash computation Mar 24, 2026
@antoinecellerier
Copy link
Copy Markdown
Author

I've reworked this to fix the actual root cause instead.

The issue: when custom_sdkconfig = file://sdkconfig.custom, the hash in matching_custom_sdkconfig() is computed from the literal string "file://sdkconfig.custom" rather than the file's contents. Editing the file doesn't change the hash, so the existing change detection never fires and stale libs are silently reused.

The fix resolves file:// references before hashing in both arduino.py and espidf.py, so that a file content change produces a different hash → triggers the existing Reinstall path (which already calls safe_remove_sdkconfig_files()). No files are deleted outside the normal flow.

The file:// path matters because it's the only way to pass Kconfig disable syntax like # CONFIG_BT_ENABLED is not set — the # is stripped by the INI parser when used inline.

Tested end-to-end: editing sdkconfig.custom to disable BT now correctly triggers recompilation and removes the 55KB BTDM DRAM reservation.

I've also updated the PR title & description to match.

@Jason2866
Copy link
Copy Markdown

Well, it is possible to disable Bluetooth, in the platformio.ini file. Needs quoting.

custom_sdkconfig            = 
                              '# CONFIG_BT_ENABLED is not set'
                              '# CONFIG_BT_NIMBLE_ENABLED is not set'
                              '# CONFIG_BT_CONTROLLER_ENABLED is not set'
                              CONFIG_BT_CONTROLLER_DISABLED=y

Anyways i will review the PR

@Jason2866
Copy link
Copy Markdown

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@builder/frameworks/espidf.py`:
- Around line 782-792: The combined-config hashing currently uses resolved
content from load_custom_sdkconfig_file() for all schemes (file/http/https), but
arduino.py's matching_custom_sdkconfig() still hashes the literal URL, causing
cache misses for URL-backed configs; to fix, change the logic that builds
custom_sdk_config_flags (the custom_sdk_config_flags variable in
builder/frameworks/espidf.py where raw =
env.GetProjectOption("custom_sdkconfig") and resolved =
load_custom_sdkconfig_file()) so it only expands/resolves and concatenates
file:// entries (local file content) into the checksum input, leaving http://
and https:// entries as their original literal strings, or alternatively update
matching_custom_sdkconfig() in builder/frameworks/arduino.py to resolve URLs the
same way—pick one approach and implement it so both sides use the same
representation for URL-backed custom_sdkconfig entries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7198aee8-d921-4421-aeeb-2ee32db06fa8

📥 Commits

Reviewing files that changed from the base of the PR and between ecdc539 and bbc82a1.

📒 Files selected for processing (2)
  • builder/frameworks/arduino.py
  • builder/frameworks/espidf.py

Comment on lines +782 to +792
# Write final configuration file with checksum.
# Include resolved file content (not just the raw "file://..." reference)
# so that editing the referenced file changes the hash and triggers
# recompilation. Combine file content + inline options to match what
# build_idf_config_flags() produces.
custom_sdk_config_flags = ""
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
custom_sdk_config_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n") + "\n"

raw = env.GetProjectOption("custom_sdkconfig")
resolved = load_custom_sdkconfig_file()
# Combine resolved file content with raw inline options (both are applied)
custom_sdk_config_flags = ((resolved + "\n") if resolved else "") + raw.rstrip("\n") + "\n"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep this new hash expansion scoped to file://, or align arduino.py with it.

load_custom_sdkconfig_file() also resolves http:// / https:// entries, but builder/frameworks/arduino.py::matching_custom_sdkconfig() still hashes the literal URL string. That means URL-backed custom_sdkconfig values will now miss the cached hash every run and keep forcing reinstall/recompile. If this PR is only fixing file://, narrow the checksum input here to local-file entries; otherwise both files should share the same resolver.

💡 Minimal fix
     if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
         raw = env.GetProjectOption("custom_sdkconfig")
-        resolved = load_custom_sdkconfig_file()
+        resolved = ""
+        for line in raw.splitlines():
+            line = line.strip()
+            if not line.startswith("file://"):
+                continue
+            file_ref = line[7:]
+            file_path = file_ref if os.path.isabs(file_ref) else str(Path(PROJECT_DIR) / file_ref)
+            if os.path.exists(file_path):
+                try:
+                    with open(file_path, "r") as f:
+                        resolved = f.read()
+                except IOError:
+                    pass
+            break
         # Combine resolved file content with raw inline options (both are applied)
         custom_sdk_config_flags = ((resolved + "\n") if resolved else "") + raw.rstrip("\n") + "\n"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Write final configuration file with checksum.
# Include resolved file content (not just the raw "file://..." reference)
# so that editing the referenced file changes the hash and triggers
# recompilation. Combine file content + inline options to match what
# build_idf_config_flags() produces.
custom_sdk_config_flags = ""
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
custom_sdk_config_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n") + "\n"
raw = env.GetProjectOption("custom_sdkconfig")
resolved = load_custom_sdkconfig_file()
# Combine resolved file content with raw inline options (both are applied)
custom_sdk_config_flags = ((resolved + "\n") if resolved else "") + raw.rstrip("\n") + "\n"
# Write final configuration file with checksum.
# Include resolved file content (not just the raw "file://..." reference)
# so that editing the referenced file changes the hash and triggers
# recompilation. Combine file content + inline options to match what
# build_idf_config_flags() produces.
custom_sdk_config_flags = ""
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
raw = env.GetProjectOption("custom_sdkconfig")
resolved = ""
for line in raw.splitlines():
line = line.strip()
if not line.startswith("file://"):
continue
file_ref = line[7:]
file_path = file_ref if os.path.isabs(file_ref) else str(Path(PROJECT_DIR) / file_ref)
if os.path.exists(file_path):
try:
with open(file_path, "r") as f:
resolved = f.read()
except IOError:
pass
break
# Combine resolved file content with raw inline options (both are applied)
custom_sdk_config_flags = ((resolved + "\n") if resolved else "") + raw.rstrip("\n") + "\n"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@builder/frameworks/espidf.py` around lines 782 - 792, The combined-config
hashing currently uses resolved content from load_custom_sdkconfig_file() for
all schemes (file/http/https), but arduino.py's matching_custom_sdkconfig()
still hashes the literal URL, causing cache misses for URL-backed configs; to
fix, change the logic that builds custom_sdk_config_flags (the
custom_sdk_config_flags variable in builder/frameworks/espidf.py where raw =
env.GetProjectOption("custom_sdkconfig") and resolved =
load_custom_sdkconfig_file()) so it only expands/resolves and concatenates
file:// entries (local file content) into the checksum input, leaving http://
and https:// entries as their original literal strings, or alternatively update
matching_custom_sdkconfig() in builder/frameworks/arduino.py to resolve URLs the
same way—pick one approach and implement it so both sides use the same
representation for URL-backed custom_sdkconfig entries.

@Jason2866
Copy link
Copy Markdown

@coderabbitai Make a suggestion in a extra PR. Do not check the complete file for changes. Thats expensive regarding performance. Store the file time and invalidate the hash if the file time has changed

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look at matching_custom_sdkconfig and related hash logic in arduino.py
rg -n "matching_custom_sdkconfig\|custom_sdkconfig\|sdk_hash\|mtime\|file://" builder/frameworks/arduino.py -A 5 -B 2

echo "---espidf.py---"
rg -n "matching_custom_sdkconfig\|custom_sdkconfig\|sdk_hash\|mtime\|file://" builder/frameworks/espidf.py -A 5 -B 2

Length of output: 15


🏁 Script executed:

#!/bin/bash
echo "=== arduino.py custom_sdkconfig ==="
rg -n "custom_sdkconfig\|matching_custom\|sdk_hash\|PathCache\|sdk_dir\|load_custom" builder/frameworks/arduino.py -A 4 -B 2

echo "=== espidf.py custom_sdkconfig ==="
rg -n "custom_sdkconfig\|matching_custom\|sdk_hash\|load_custom" builder/frameworks/espidf.py -A 4 -B 2

echo "=== file structure ==="
ls builder/frameworks/

Length of output: 167


🏁 Script executed:

#!/bin/bash
echo "=== Searching for hash/sdkconfig patterns across all builder files ==="
rg -n "hash\|sdkconfig\|file://" builder/ -l

echo "=== arduino.py snippet (first 100 lines) ==="
head -100 builder/frameworks/arduino.py

echo "=== Search for 'hash' in arduino.py ==="
grep -n "hash\|sdkconfig\|mtime\|getmtime\|st_mtime" builder/frameworks/arduino.py | head -60

echo "=== Search for 'hash' in espidf.py ==="
grep -n "hash\|sdkconfig\|mtime\|getmtime\|st_mtime" builder/frameworks/espidf.py | head -60

Length of output: 10581


🏁 Script executed:

#!/bin/bash
echo "=== arduino.py: file:// resolution and hash logic (lines 260-420) ==="
sed -n '260,420p' builder/frameworks/arduino.py

echo ""
echo "=== espidf.py: load_custom_sdkconfig_file and checksum logic (lines 265,710-800) ==="
sed -n '265,315p' builder/frameworks/espidf.py
echo "---"
sed -n '705,800p' builder/frameworks/espidf.py

Length of output: 12042


@Jason2866 — understood! Here's a concrete plan for the mtime-based alternative. The key idea: instead of reading the file's contents into the hash input, just do a stat() call and include the mtime float string. This is already the pattern used in espidf.py at lines 872–878 for cmake cache comparisons.


builder/frameworks/arduino.py — replace content-read with mtime

Lines ~282–298 (PR #441 version → mtime version):

# Custom SDKConfig check
if config.has_option(current_env_section, "custom_sdkconfig"):
    entry_custom_sdkconfig = env.GetProjectOption("custom_sdkconfig")
    # For file:// references use mtime (cheap stat) instead of reading
    # the full file contents. A changed mtime means a different hash,
    # which triggers the existing Reinstall path.
    for line in entry_custom_sdkconfig.splitlines():
        line = line.strip()
        if line.startswith("file://"):
            file_ref = line[7:]
            file_path = file_ref if isabs(file_ref) else join(project_dir, file_ref)
            try:
                mtime = str(os.path.getmtime(file_path))
                entry_custom_sdkconfig = mtime + "\n" + entry_custom_sdkconfig
            except OSError:
                pass
            break
    flag_custom_sdkconfig = True

builder/frameworks/espidf.py — replace content-read with mtime

Lines ~784–793 (PR #441 version → mtime version):

custom_sdk_config_flags = ""
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
    raw = env.GetProjectOption("custom_sdkconfig")
    # For file:// references use mtime (cheap stat) instead of file contents.
    file_mtime = ""
    for entry in raw.splitlines():
        entry = entry.strip()
        if entry.startswith("file://"):
            file_ref = entry[7:]
            file_path = file_ref if os.path.isabs(file_ref) else str(Path(PROJECT_DIR) / file_ref)
            try:
                file_mtime = str(os.path.getmtime(file_path))
            except OSError:
                pass
            break
    custom_sdk_config_flags = (file_mtime + "\n" if file_mtime else "") + raw.rstrip("\n") + "\n"

Summary of tradeoffs vs. PR #441's content-hashing approach

PR #441 (content hash) mtime approach
Cost Reads entire file Single stat() call
Miss change? Never (hash reflects exact content) Only if mtime is not updated (e.g. cp --preserve=timestamps)
False positive rebuild? No Yes if file is touch-ed without content change
Pattern already in codebase No Yes (espidf.py lines 872–878)

The mtime approach is the right trade-off for this use case — users editing sdkconfig.custom will always get a new mtime, and false-positive rebuilds (from a plain touch) are harmless.

Would you like me to open a new PR with these changes?


🧠 Learnings used
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 281
File: builder/frameworks/component_manager.py:1047-1054
Timestamp: 2025-09-04T15:27:18.112Z
Learning: In the pioarduino/platform-espressif32 project, Jason2866 prefers not to add guard checks for missing arduino_libs_mcu in backup operations, preferring to let the operation proceed rather than silently skip when the directory might be missing.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 199
File: examples/arduino-blink/platformio.ini:81-83
Timestamp: 2025-06-26T09:56:30.658Z
Learning: In examples/arduino-blink/platformio.ini, the different lib_ignore configurations across environments (some including wifi, some not, different syntax styles) are intentional test cases, not inconsistencies that need to be fixed.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 432
File: builder/frameworks/espidf.py:1167-1179
Timestamp: 2026-03-15T15:31:38.160Z
Learning: In pioarduino/platform-espressif32, response file paths (`file` tokens) emitted by the ESP-IDF/CMake codemodel are always absolute paths. There is no need to resolve relative paths when handling response file expansion in `get_app_flags` or `prepare_build_envs` in `builder/frameworks/espidf.py`. The existing `os.path.isfile(resp_path)` check is sufficient.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 285
File: platform.py:670-683
Timestamp: 2025-09-09T18:07:14.583Z
Learning: In the pioarduino/platform-espressif32 project, when tools.json exists in mklittlefs tool directories, it triggers a reinstall via install_tool(). This is intentional behavior to convert tools installed via idf_tools.py method into the platform's tool management system, rather than a redundant check that should be avoided.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 0
File: :0-0
Timestamp: 2026-03-07T22:12:49.994Z
Learning: In monitor/filter_exception_decoder.py for pioarduino/platform-espressif32: the `is_return_addr` / `-1` decrement in `_resolve_address` and `_prefetch_addresses` was intentionally removed in PR 419. ESP-IDF's panic handler already provides call-site adjusted addresses; decrementing again produces wrong function/line results. On RISC-V with compressed instructions, subtracting 1 byte can land mid-instruction. Stack trace words should never be blindly decremented — `PcAddressMatcher` ELF section filtering is the correct mechanism. Cache keys must use the original address string (not the decremented lookup) for prefetch/resolve consistency.

@Jason2866
Copy link
Copy Markdown

@coderabbitai Open a new PR with the mtime aproach.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 24, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@Jason2866 — I'll gather the current state of the develop branch files first, then plan the new PR.


🏁 Script executed:

#!/bin/bash
# Check which branch we're on and get current file state
git branch --show-current
echo "=== arduino.py: custom_sdkconfig and hash region ==="
grep -n "custom_sdkconfig\|file://\|mtime\|PathCache\|sdk_dir\|framework_lib_dir\|flag_custom_sdkconfig\|entry_custom_sdkconfig\|matching_custom" builder/frameworks/arduino.py | head -60

echo ""
echo "=== arduino.py lines 270-330 ==="
sed -n '270,330p' builder/frameworks/arduino.py

echo ""
echo "=== espidf.py: custom_sdkconfig hash region ==="
grep -n "custom_sdk_config_flags\|custom_sdkconfig\|file://\|load_custom_sdkconfig" builder/frameworks/espidf.py | head -40

echo ""
echo "=== espidf.py lines 260-310 ==="
sed -n '260,310p' builder/frameworks/espidf.py

echo ""
echo "=== espidf.py lines 775-810 ==="
sed -n '775,810p' builder/frameworks/espidf.py

echo ""
echo "=== Check imports in arduino.py ==="
head -30 builder/frameworks/arduino.py

echo ""
echo "=== Check imports in espidf.py ==="
head -30 builder/frameworks/espidf.py

Length of output: 12469


@Jason2866 — I have the current code. Here's the plan for the new PR with the mtime approach:

Task: New PR — use mtime instead of file content for custom_sdkconfig hash

  • New branch fix/sdkconfig-mtime-hash off develop
  • In builder/frameworks/arduino.py: replace the file-content read with os.path.getmtime() stat
  • In builder/frameworks/espidf.py: replace the content-hash with os.path.getmtime() stat
  • Open PR targeting develop

🏁 Script executed:

#!/bin/bash
# Fetch develop and show baseline for the two relevant regions
git fetch origin develop
git show origin/develop:builder/frameworks/arduino.py | sed -n '270,310p'
echo "---ESPIDF---"
git show origin/develop:builder/frameworks/espidf.py | sed -n '760,800p'

Length of output: 204


🏁 Script executed:

#!/bin/bash
git fetch origin develop
git checkout -b fix/sdkconfig-mtime-hash origin/develop

python3 - <<'PYEOF'
import re

# --- arduino.py patch ---
with open("builder/frameworks/arduino.py", "r") as f:
    src = f.read()

old_block = '''    # When custom_sdkconfig references a file, include its contents in the
    # value used for hash computation. Otherwise, editing the file doesn't
    # change the hash and stale libs are silently reused.
    # Combine file content with inline options (both are applied by the build).
    for line in entry_custom_sdkconfig.splitlines():
        line = line.strip()
        if line.startswith("file://"):
            file_ref = line[7:]
            file_path = file_ref if isabs(file_ref) else join(project_dir, file_ref)
            if exists(file_path):
                try:
                    with open(file_path, \'r\') as f:
                        file_content = f.read()
                    entry_custom_sdkconfig = file_content + "\\n" + entry_custom_sdkconfig
                except IOError:
                    pass
            break'''

new_block = '''    # When custom_sdkconfig references a file, include its mtime in the
    # value used for hash computation. A changed mtime means a new hash
    # and triggers the existing Reinstall path. Using mtime (a single
    # stat() call) is cheaper than reading the full file contents.
    for line in entry_custom_sdkconfig.splitlines():
        line = line.strip()
        if line.startswith("file://"):
            file_ref = line[7:]
            file_path = file_ref if isabs(file_ref) else join(project_dir, file_ref)
            try:
                mtime = str(os.path.getmtime(file_path))
                entry_custom_sdkconfig = mtime + "\\n" + entry_custom_sdkconfig
            except OSError:
                pass
            break'''

if old_block in src:
    src = src.replace(old_block, new_block)
    with open("builder/frameworks/arduino.py", "w") as f:
        f.write(src)
    print("arduino.py patched OK")
else:
    print("arduino.py: block NOT FOUND")
    # Print what's there for debug
    idx = src.find("When custom_sdkconfig references a file")
    print(repr(src[max(0,idx-50):idx+500]))
PYEOF

python3 - <<'PYEOF'
import os

# --- espidf.py patch ---
with open("builder/frameworks/espidf.py", "r") as f:
    src = f.read()

old_block = '''    # Include resolved file content (not just the raw "file://..." reference)
    # so that editing the referenced file changes the hash and triggers
    # recompilation. Combine file content + inline options to match what
    # build_idf_config_flags() produces.
    custom_sdk_config_flags = ""
    if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
        raw = env.GetProjectOption("custom_sdkconfig")
        resolved = load_custom_sdkconfig_file()
        # Combine resolved file content with raw inline options (both are applied)
        custom_sdk_config_flags = ((resolved + "\\n") if resolved else "") + raw.rstrip("\\n") + "\\n"'''

new_block = '''    # Include the mtime of any referenced file (not just the raw "file://..."
    # string) so that editing the file changes the hash and triggers
    # recompilation. A single stat() call is cheaper than reading the full
    # file contents.
    custom_sdk_config_flags = ""
    if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
        raw = env.GetProjectOption("custom_sdkconfig")
        file_mtime = ""
        for entry in raw.splitlines():
            entry = entry.strip()
            if entry.startswith("file://"):
                file_ref = entry[7:]
                file_path = file_ref if os.path.isabs(file_ref) else str(Path(PROJECT_DIR) / file_ref)
                try:
                    file_mtime = str(os.path.getmtime(file_path))
                except OSError:
                    pass
                break
        custom_sdk_config_flags = (file_mtime + "\\n" if file_mtime else "") + raw.rstrip("\\n") + "\\n"'''

if old_block in src:
    src = src.replace(old_block, new_block)
    with open("builder/frameworks/espidf.py", "w") as f:
        f.write(src)
    print("espidf.py patched OK")
else:
    print("espidf.py: block NOT FOUND")
    idx = src.find("Include resolved file content")
    print(repr(src[max(0,idx-50):idx+600]))
PYEOF

git diff --stat
git add builder/frameworks/arduino.py builder/frameworks/espidf.py
git commit -m "Fix: use mtime (not file contents) for custom_sdkconfig file:// hash

When custom_sdkconfig = file://sdkconfig.custom, the hash used to
detect configuration changes was computed from the literal string
\"file://sdkconfig.custom\". Editing the referenced file did not
change the hash, so matching_custom_sdkconfig() returned True and
stale precompiled libs were silently reused.

This fixes the issue by including the file's mtime in the hash input
instead of its full contents. A single stat() call is cheaper than
reading the entire file, and normal editor saves always update mtime.
The pattern matches mtime-based cache invalidation already used
elsewhere in this codebase.

False-positive rebuilds (e.g. from a bare touch) are harmless.
Inline (non-file://) custom_sdkconfig values are unchanged."

git push origin fix/sdkconfig-mtime-hash

gh pr create \
  --title "Fix: use mtime for custom_sdkconfig file:// hash (cheaper than content read)" \
  --body "## Problem

When \`custom_sdkconfig = file://sdkconfig.custom\`, the hash used to detect configuration changes was computed from the literal string \`\"file://sdkconfig.custom\"\` rather than anything reflecting the file's contents. Editing the referenced file did not change the hash, causing \`matching_custom_sdkconfig()\` to return \`True\` and stale precompiled libs to be reused silently.

## Fix

Include the file's **mtime** (via a single \`os.path.getmtime()\` stat call) in the hash input for both \`arduino.py\` and \`espidf.py\`. Editing the file updates its mtime → different hash → existing Reinstall path fires.

## Why mtime instead of file contents

| | Content hash (PR `#441`) | **mtime (this PR)** |
|---|---|---|
| Cost | Reads entire file on every build | Single \`stat()\` call |
| Miss a change? | Never | Only if mtime is preserved (e.g. \`cp --preserve=timestamps\`) — harmless in practice |
| False positive rebuild? | No | Yes, on bare \`touch\` — but that is harmless |
| Pattern already in codebase? | No | Yes — \`espidf.py\` already uses mtime for cmake-cache invalidation |

## Scope

- Only affects \`file://\` references; inline \`custom_sdkconfig\` values are unchanged.
- Actual \`sdkconfig.defaults\` generation (\`load_custom_sdkconfig_file()\`) is unaffected — only the checksum input changes.

Closes the change-detection gap reported in `#441` with a lighter-weight approach suggested by `@Jason2866`." \
  --base develop \
  --head fix/sdkconfig-mtime-hash \
  --repo pioarduino/platform-espressif32

Length of output: 943


🏁 Script executed:

#!/bin/bash
# Find available remote branches and default branch
git remote show origin 2>/dev/null | grep "HEAD branch\|Tracked"
git branch -r | head -20
echo "=== default branch via gh ==="
gh repo view pioarduino/platform-espressif32 --json defaultBranchRef --jq '.defaultBranchRef.name'
echo "=== all remote branches ==="
gh api repos/pioarduino/platform-espressif32/branches --jq '.[].name'

Length of output: 197


🏁 Script executed:

#!/bin/bash
set -e

# Configure git identity
git config --global user.email "coderabbitai[bot]@users.noreply.github.com"
git config --global user.name "coderabbitai[bot]"

# Fetch develop branch explicitly
git fetch origin refs/heads/develop:refs/remotes/origin/develop

# Create new branch from develop
git checkout -b fix/sdkconfig-mtime-hash origin/develop

echo "=== Current branch ==="
git branch --show-current

echo "=== arduino.py lines 278-302 on develop ==="
sed -n '278,302p' builder/frameworks/arduino.py

echo "=== espidf.py custom_sdkconfig hash region on develop ==="
grep -n "custom_sdk_config_flags\|custom_sdkconfig\|file://\|load_custom_sdkconfig" builder/frameworks/espidf.py | grep -v "^.*#" | head -20
sed -n '778,798p' builder/frameworks/espidf.py

Length of output: 327


🏁 Script executed:

#!/bin/bash
set -e

git config --global user.email "coderabbitai[bot]@users.noreply.github.com"
git config --global user.name "coderabbitai[bot]"

# Stash the previously applied patches, switch to develop, re-apply
git stash
git checkout -b fix/sdkconfig-mtime-hash origin/develop

echo "=== On branch ==="
git branch --show-current

echo "=== arduino.py custom_sdkconfig block on develop (lines 278-310) ==="
sed -n '278,310p' builder/frameworks/arduino.py

echo "---ESPIDF---"
grep -n "custom_sdk_config_flags\|load_custom_sdkconfig_file\|write_sdkconfig_file\|file://" builder/frameworks/espidf.py | tail -20
echo "=== espidf.py last ~30 lines of HandleArduinoIDFsettings ==="
sed -n '775,810p' builder/frameworks/espidf.py

Length of output: 3460


Now I have the clean develop baseline. Applying the mtime patches and opening the PR.


🏁 Script executed:

#!/bin/bash
set -e

# Apply mtime patch to arduino.py
python3 - <<'PYEOF'
with open("builder/frameworks/arduino.py", "r") as f:
    src = f.read()

old_block = '''if config.has_option(current_env_section, "custom_sdkconfig"):
    entry_custom_sdkconfig = env.GetProjectOption("custom_sdkconfig")
    flag_custom_sdkconfig = True'''

new_block = '''if config.has_option(current_env_section, "custom_sdkconfig"):
    entry_custom_sdkconfig = env.GetProjectOption("custom_sdkconfig")
    # When custom_sdkconfig references a file, include its mtime in the
    # value used for hash computation. A changed mtime means a new hash
    # and triggers the existing Reinstall path. Using mtime (a single
    # stat() call) is cheaper than reading the full file contents.
    for line in entry_custom_sdkconfig.splitlines():
        line = line.strip()
        if line.startswith("file://"):
            file_ref = line[7:]
            file_path = file_ref if isabs(file_ref) else join(project_dir, file_ref)
            try:
                mtime = str(os.path.getmtime(file_path))
                entry_custom_sdkconfig = mtime + "\\n" + entry_custom_sdkconfig
            except OSError:
                pass
            break
    flag_custom_sdkconfig = True'''

if old_block in src:
    src = src.replace(old_block, new_block, 1)
    with open("builder/frameworks/arduino.py", "w") as f:
        f.write(src)
    print("arduino.py patched OK")
else:
    print("ERROR: arduino.py block not found")
    idx = src.find("entry_custom_sdkconfig = env.GetProjectOption")
    print(repr(src[max(0,idx-100):idx+200]))
PYEOF

# Apply mtime patch to espidf.py
python3 - <<'PYEOF'
with open("builder/frameworks/espidf.py", "r") as f:
    src = f.read()

old_block = '''    custom_sdk_config_flags = ""
    if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
        custom_sdk_config_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\\n") + "\\n"
    
    write_sdkconfig_file(idf_config_list, custom_sdk_config_flags)'''

new_block = '''    # Include the mtime of any referenced file (not just the raw "file://..."
    # string) so that editing the file changes the hash and triggers
    # recompilation. A single stat() call is cheaper than reading the full
    # file contents.
    custom_sdk_config_flags = ""
    if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
        raw = env.GetProjectOption("custom_sdkconfig")
        file_mtime = ""
        for entry in raw.splitlines():
            entry = entry.strip()
            if entry.startswith("file://"):
                file_ref = entry[7:]
                file_path = file_ref if os.path.isabs(file_ref) else str(Path(PROJECT_DIR) / file_ref)
                try:
                    file_mtime = str(os.path.getmtime(file_path))
                except OSError:
                    pass
                break
        custom_sdk_config_flags = (file_mtime + "\\n" if file_mtime else "") + raw.rstrip("\\n") + "\\n"
    
    write_sdkconfig_file(idf_config_list, custom_sdk_config_flags)'''

if old_block in src:
    src = src.replace(old_block, new_block, 1)
    with open("builder/frameworks/espidf.py", "w") as f:
        f.write(src)
    print("espidf.py patched OK")
else:
    print("ERROR: espidf.py block not found — trying relaxed match")
    idx = src.find("custom_sdk_config_flags = env.GetProjectOption")
    print(repr(src[max(0,idx-200):idx+300]))
PYEOF

echo "=== Diff preview ==="
git diff

echo "=== Committing ==="
git add builder/frameworks/arduino.py builder/frameworks/espidf.py
git commit -m "Fix: use mtime for custom_sdkconfig file:// hash

When custom_sdkconfig = file://sdkconfig.custom, the hash used to
detect configuration changes was computed from the literal string
'file://sdkconfig.custom'. Editing the referenced file did not
change the hash, so matching_custom_sdkconfig() returned True and
stale precompiled libs were silently reused.

Fix: include the file's mtime (via a single os.path.getmtime() stat
call) in the hash input for both arduino.py and espidf.py. Editing
the file updates its mtime → different hash → existing Reinstall
path fires automatically.

Using mtime is cheaper than reading the full file contents and
matches the mtime-based invalidation pattern already used in
espidf.py for cmake-cache comparisons.

False-positive rebuilds (e.g. from a bare touch) are harmless.
Inline (non-file://) custom_sdkconfig values are unchanged."

echo "=== Pushing ==="
git push origin fix/sdkconfig-mtime-hash

echo "=== Opening PR ==="
gh pr create \
  --title "Fix: use mtime for custom_sdkconfig file:// hash" \
  --body "## Problem

When \`custom_sdkconfig = file://sdkconfig.custom\`, the hash used to detect configuration changes was computed from the literal string \`\"file://sdkconfig.custom\"\` — not from anything that reflects the file's contents. Editing the referenced file did not change the hash, causing \`matching_custom_sdkconfig()\` to return \`True\` and stale precompiled libs to be reused silently.

## Fix

Include the file's **mtime** (via a single \`os.path.getmtime()\` stat call) in the hash input for both \`arduino.py\` and \`espidf.py\`. Editing the file updates its mtime → different hash → existing Reinstall path fires.

## Why mtime instead of reading file contents

| | Read file contents | **mtime (this PR)** |
|---|---|---|
| Cost per build | Reads entire file | Single \`stat()\` call |
| Miss a change? | Never | Only if mtime is preserved (e.g. \`cp --preserve=timestamps\`) |
| False positive rebuild? | No | Yes on bare \`touch\` — harmless |
| Pattern in codebase? | No | Yes — \`espidf.py\` already uses mtime for cmake-cache invalidation |

## Scope

- Only affects \`file://\` references; inline \`custom_sdkconfig\` values are unchanged.
- Actual \`sdkconfig.defaults\` generation (\`load_custom_sdkconfig_file()\`) is unaffected — only the checksum input changes.

Alternative to `#441`, implementing the lighter-weight approach suggested by `@Jason2866`." \
  --base develop \
  --head fix/sdkconfig-mtime-hash \
  --repo pioarduino/platform-espressif32

Length of output: 3196


The commit is ready — the push must have failed silently. Retrying push and PR creation now.


🏁 Script executed:

#!/bin/bash
echo "=== Current branch & commit ==="
git branch --show-current
git log --oneline -3

echo "=== Pushing branch ==="
git push origin fix/sdkconfig-mtime-hash 2>&1
echo "Push exit code: $?"

echo "=== Verify branch on remote ==="
gh api repos/pioarduino/platform-espressif32/branches --jq '.[].name' 2>&1

echo "=== Opening PR ==="
gh pr create \
  --title "Fix: use mtime for custom_sdkconfig file:// hash" \
  --body "## Problem

When \`custom_sdkconfig = file://sdkconfig.custom\`, the hash used to detect configuration changes was computed from the literal string \`\"file://sdkconfig.custom\"\` — not from anything that reflects the file's contents. Editing the referenced file did not change the hash, causing \`matching_custom_sdkconfig()\` to return \`True\` and stale precompiled libs to be reused silently.

## Fix

Include the file's **mtime** (via a single \`os.path.getmtime()\` stat call) in the hash input for both \`arduino.py\` and \`espidf.py\`. Editing the file updates its mtime → different hash → existing Reinstall path fires.

## Why mtime instead of reading file contents

| | Read file contents | **mtime (this PR)** |
|---|---|---|
| Cost per build | Reads entire file | Single \`stat()\` call |
| Miss a change? | Never | Only if mtime is preserved (e.g. \`cp --preserve=timestamps\`) |
| False positive rebuild? | No | Yes on bare \`touch\` — harmless |
| Pattern in codebase? | No | Yes — \`espidf.py\` already uses mtime for cmake-cache invalidation |

## Scope

- Only affects \`file://\` references; inline \`custom_sdkconfig\` values are unchanged.
- Actual \`sdkconfig.defaults\` generation (\`load_custom_sdkconfig_file()\`) is unaffected — only the checksum input changes.

Alternative to `#441`, implementing the lighter-weight approach suggested by `@Jason2866`." \
  --base develop \
  --head fix/sdkconfig-mtime-hash \
  --repo pioarduino/platform-espressif32 2>&1

Length of output: 613


🏁 Script executed:

#!/bin/bash
echo "=== Local commit SHA ==="
git rev-parse HEAD
git log --oneline -1

echo "=== Remote ref for our branch ==="
git ls-remote origin fix/sdkconfig-mtime-hash

echo "=== Remote develop SHA ==="
git ls-remote origin develop

echo "=== Verbose push ==="
git push -v --force origin fix/sdkconfig-mtime-hash 2>&1

echo "=== ls-remote after push ==="
git ls-remote origin fix/sdkconfig-mtime-hash

echo "=== Try PR creation via GitHub API directly ==="
BRANCH_SHA=$(git rev-parse HEAD)
echo "Branch SHA: $BRANCH_SHA"

# Create/update the branch ref via API
gh api \
  --method POST \
  repos/pioarduino/platform-espressif32/git/refs \
  -f ref="refs/heads/fix/sdkconfig-mtime-hash" \
  -f sha="$BRANCH_SHA" 2>&1 || \
gh api \
  --method PATCH \
  "repos/pioarduino/platform-espressif32/git/refs/heads/fix/sdkconfig-mtime-hash" \
  -f sha="$BRANCH_SHA" \
  -F force=true 2>&1

Length of output: 732


@Jason2866 — apologies, the sandbox environment can push branches but the commit object never reaches GitHub (the remote confirms it doesn't exist). I can't open the PR directly from here.

Here are the exact diffs to apply — both are minimal and self-contained:


builder/frameworks/arduino.py — after entry_custom_sdkconfig = env.GetProjectOption("custom_sdkconfig"), insert the mtime block:

 if config.has_option(current_env_section, "custom_sdkconfig"):
     entry_custom_sdkconfig = env.GetProjectOption("custom_sdkconfig")
+    # When custom_sdkconfig references a file, include its mtime in the
+    # value used for hash computation. A changed mtime means a new hash
+    # and triggers the existing Reinstall path. Using mtime (a single
+    # stat() call) is cheaper than reading the full file contents.
+    for line in entry_custom_sdkconfig.splitlines():
+        line = line.strip()
+        if line.startswith("file://"):
+            file_ref = line[7:]
+            file_path = file_ref if isabs(file_ref) else join(project_dir, file_ref)
+            try:
+                mtime = str(os.path.getmtime(file_path))
+                entry_custom_sdkconfig = mtime + "\n" + entry_custom_sdkconfig
+            except OSError:
+                pass
+            break
     flag_custom_sdkconfig = True

builder/frameworks/espidf.py — replace the custom_sdk_config_flags block inside HandleArduinoIDFsettings:

-    custom_sdk_config_flags = ""
-    if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
-        custom_sdk_config_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n") + "\n"
+    # Include the mtime of any referenced file (not just the raw "file://..."
+    # string) so that editing the file changes the hash and triggers
+    # recompilation. A single stat() call is cheaper than reading the full
+    # file contents.
+    custom_sdk_config_flags = ""
+    if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
+        raw = env.GetProjectOption("custom_sdkconfig")
+        file_mtime = ""
+        for entry in raw.splitlines():
+            entry = entry.strip()
+            if entry.startswith("file://"):
+                file_ref = entry[7:]
+                file_path = file_ref if os.path.isabs(file_ref) else str(Path(PROJECT_DIR) / file_ref)
+                try:
+                    file_mtime = str(os.path.getmtime(file_path))
+                except OSError:
+                    pass
+                break
+        custom_sdk_config_flags = (file_mtime + "\n" if file_mtime else "") + raw.rstrip("\n") + "\n"

     write_sdkconfig_file(idf_config_list, custom_sdk_config_flags)

Both files already import os, and isabs/join/Path/PROJECT_DIR are all in scope. No new imports needed. The PR can be opened against develop with the commit message:

Fix: use mtime for custom_sdkconfig file:// hash

When custom_sdkconfig = file://sdkconfig.custom the hash was computed
from the literal string "file://sdkconfig.custom", not the file's
contents. Editing the file did not change the hash, so stale libs
were silently reused.

Fix: prepend os.path.getmtime() result to the hash input. A single
stat() call is cheaper than reading the full file and matches the
mtime-based invalidation pattern already used in espidf.py.

Inline (non-file://) custom_sdkconfig values are unchanged.

🧠 Learnings used
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 281
File: builder/frameworks/component_manager.py:1047-1054
Timestamp: 2025-09-04T15:27:18.112Z
Learning: In the pioarduino/platform-espressif32 project, Jason2866 prefers not to add guard checks for missing arduino_libs_mcu in backup operations, preferring to let the operation proceed rather than silently skip when the directory might be missing.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 0
File: :0-0
Timestamp: 2026-03-07T22:12:49.994Z
Learning: In monitor/filter_exception_decoder.py for pioarduino/platform-espressif32: the `is_return_addr` / `-1` decrement in `_resolve_address` and `_prefetch_addresses` was intentionally removed in PR 419. ESP-IDF's panic handler already provides call-site adjusted addresses; decrementing again produces wrong function/line results. On RISC-V with compressed instructions, subtracting 1 byte can land mid-instruction. Stack trace words should never be blindly decremented — `PcAddressMatcher` ELF section filtering is the correct mechanism. Cache keys must use the original address string (not the decremented lookup) for prefetch/resolve consistency.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 268
File: builder/penv_setup.py:87-96
Timestamp: 2025-08-26T14:52:29.044Z
Learning: For the pioarduino/platform-espressif32 project, Jason2866 prefers to trust system executables when falling back to PATH-based command resolution (like "uv"), rather than implementing additional validation checks for security concerns.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 432
File: builder/frameworks/espidf.py:1167-1179
Timestamp: 2026-03-15T15:31:38.160Z
Learning: In pioarduino/platform-espressif32, response file paths (`file` tokens) emitted by the ESP-IDF/CMake codemodel are always absolute paths. There is no need to resolve relative paths when handling response file expansion in `get_app_flags` or `prepare_build_envs` in `builder/frameworks/espidf.py`. The existing `os.path.isfile(resp_path)` check is sufficient.

Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 371
File: builder/spiffsgen.py:620-629
Timestamp: 2025-12-27T16:33:38.710Z
Learning: In the pioarduino/platform-espressif32 project, when extracting SPIFFS files in builder/spiffsgen.py, Jason2866 prefers to check if rel_path is empty after lstrip('/') and skip such files with a warning message, rather than attempting to handle empty parent directories in os.makedirs calls. This treats empty paths as invalid/corrupted data.

Failed to handle agent chat message. Please try again.

@Jason2866
Copy link
Copy Markdown

@antoinecellerier Please switch to the mtime approach. The AI has already posted the probably working code diffs.

@antoinecellerier
Copy link
Copy Markdown
Author

@Jason2866 I'd thought about that as well but that would lead to different hashes for no-op changes, so likely more rebuilds than necessary right? Implementation would be simpler though.

@Jason2866
Copy link
Copy Markdown

Jason2866 commented Mar 25, 2026

The probably very seldom wrong rebuilds are way more acceptable than the performance hit by scanning ALWAYS the sdkconfig file by every compile run. In other words i will accept only the mtime approach which is a bug fix!

@Jason2866
Copy link
Copy Markdown

Thx for the bug finding. Closing with implementing in #446

@Jason2866 Jason2866 closed this Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants