[core] Put ComponentMarker outside the IIFE as a visual bracket

The marker comment was being emitted as the first line *inside* each
IIFE:

  []() {
    // === logger ===
    // logger:
    //   ...
    ...
  }();

That works but buries the component label inside the lambda body, so
scanning generated main.cpp to find "where does component X's setup
live" is harder than it needs to be. Emit the marker before and after
the IIFE instead:

  // === logger ===
  []() {
    // logger:
    //   ...
    ...
  }();
  // === logger ===

Comment-only components (e.g. sha256, async_tcp, empty platforms like
binary_sensor:) don't grow a useless trailing duplicate marker —
when there's no IIFE to bracket, the marker is emitted once.
This commit is contained in:
J. Nick Koston
2026-04-17 14:53:59 -05:00
parent 936694af2c
commit 864d31aa65
2 changed files with 35 additions and 11 deletions
+18 -9
View File
@@ -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
+17 -2
View File
@@ -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: