diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index b66899e1338..acf2e197ac1 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -1052,13 +1052,16 @@ class EsphomeCore: # Split main_statements at ComponentMarker sentinels into a prefix # (statements emitted before any component) plus per-component groups. + # Each group's first entry is its marker comment, kept separate so + # it can bracket the IIFE rather than be buried inside it. prefix: list[str] = [] - components: list[list[str]] = [] + components: list[tuple[str, list[str]]] = [] current = prefix for exp in self.main_statements: if isinstance(exp, ComponentMarker): - current = [str(exp).rstrip()] - components.append(current) + body: list[str] = [] + components.append((str(exp).rstrip(), body)) + current = body continue current.append(str(statement(exp)).rstrip()) @@ -1066,13 +1069,19 @@ class EsphomeCore: if not components: return "\n".join(prefix) + "\n\n" - # Each component's block is wrapped in a noinline IIFE lambda so its - # stack frame is released on return, bounding peak stack during - # setup(). Large blocks are sub-split to cap single heavy components - # (e.g. sensor platforms with many filter registrations). + # Each component's block is wrapped in IIFE lambdas so its stack + # frame is released on return, bounding peak stack during setup(). + # Large blocks are sub-split to cap single heavy components (e.g. + # sensor platforms with many filter registrations). The marker + # comment brackets the IIFE on both sides so the generated + # main.cpp is easy to scan by component. pieces = list(prefix) - for block in components: - pieces.extend(_wrap_in_iifes(block, max_statements=50)) + for marker, body in components: + wrapped = _wrap_in_iifes(body, max_statements=50) + pieces.append(marker) + pieces.extend(wrapped) + if any("[]()" in line for line in wrapped): + pieces.append(marker) return "\n".join(pieces) + "\n\n" @property diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index cd65e670ed7..dc16d4cfe9c 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -961,8 +961,23 @@ def test_cpp_main_section_component_marker_wraps_in_iife() -> None: out = target.cpp_main_section assert out.count("[]() {") == 2 assert out.count("}();") == 2 - assert "// === logger ===" in out - assert "// === wifi ===" in out + # Each component's marker brackets its IIFE (once before, once after). + assert out.count("// === logger ===") == 2 + assert out.count("// === wifi ===") == 2 + + +def test_cpp_main_section_comment_only_component_emits_single_marker() -> None: + # A component that emits no C++ statements (only a ComponentMarker) + # should not grow a useless trailing duplicate marker. + target = core.EsphomeCore() + target.main_statements = [ + ComponentMarker("sha256"), + ComponentMarker("wifi"), + RawStatement("new_wifi();"), + ] + out = target.cpp_main_section + assert out.count("// === sha256 ===") == 1 + assert out.count("// === wifi ===") == 2 # wifi has an IIFE def test_cpp_main_section_prefix_statements_stay_outside_iife() -> None: