diff --git a/esphome/__main__.py b/esphome/__main__.py index 20e522968eb..6cc89de9699 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -1961,10 +1961,8 @@ def command_update_all(args: ArgsProtocol) -> int | None: return run_multiple_configs(files, build_command) -# Native build backend per (target platform, toolchain). Keyed here rather -# than through a platform hook so the serial upload/logs fast path never -# imports the platform component package (see the esp32 variant comment in -# upload_using_esptool); the platform half comes from CORE.data the same way. +# Native build backend per (target platform, toolchain); keyed here so +# the serial upload/logs fast path never imports the platform package _NATIVE_TOOLCHAIN_MODULES = { ("esp32", Toolchain.ESP_IDF): "esphome.espidf.toolchain", ("esp8266", Toolchain.ARDUINO): "esphome.arduino8266.toolchain", @@ -2059,9 +2057,8 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int: readelf = native_toolchain.get_readelf_path() for tool in (objdump, readelf): if not tool.is_file(): - # The analyzer would silently fall back to host binutils, - # which cannot read the target ELF. clean-all is heavy for - # ESP-IDF, so suggest a recompile first. + # The analyzer would silently fall back to host + # binutils, which cannot read the target ELF _LOGGER.error( "%s is missing; the toolchain install may be incomplete " "(recompile, or run 'esphome clean-all' if it persists)", diff --git a/esphome/arduino8266/toolchain.py b/esphome/arduino8266/toolchain.py index 0d7fe516220..de684387edb 100644 --- a/esphome/arduino8266/toolchain.py +++ b/esphome/arduino8266/toolchain.py @@ -29,10 +29,8 @@ _MAX_RAM_SIZE = 81920 def _warn_ignored_platformio_options() -> None: """Warn for component-added platformio options the native build drops. - The consumed set is exported by core/config.py next to the routing that - stores these options, so the two cannot drift; YAML upload_speed never - reaches CORE.platformio_options here. - """ + The consumed set lives next to the routing in core/config.py so the + two cannot drift.""" from esphome.core.config import NATIVE_ARDUINO_CONSUMED_PIO_OPTIONS consumed = NATIVE_ARDUINO_CONSUMED_PIO_OPTIONS @@ -84,24 +82,20 @@ def run_compile(config: ConfigType, verbose: bool) -> int: _warn_ignored_platformio_options() paths = framework.check_and_install(CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]) - # Resolved once per build: the resolution probes PATH and spawns the - # runnability check, and three consumers need the same answer + # Resolved once: the probe is not free and three consumers need it ccache = resolve_ccache_path() ninja_changed = build_gen.write_project(paths, ccache) build_dir = get_build_dir() env = framework.get_build_env(paths.toolchain, ccache) - # Regenerate the compile DB before the build (a pure function of - # build.ninja); skip only when it is at least as fresh as build.ninja - # (an interrupted previous run may have rewritten the manifest without - # regenerating the DB). + # Regenerate the compile DB when it is older than build.ninja. + # Freshness rides a stamp: the DB is written via write_file_if_changed + # (mtime feeds get_idedata's cache), so identical content would + # otherwise stay "stale" forever compdb = build_dir / "compile_commands.json" compdb_stamp = build_dir / ".compile_commands.stamp" ninja_file = build_dir / "build.ninja" - # Freshness rides a stamp: the DB itself is written through - # write_file_if_changed (its mtime feeds get_idedata's cache), so a - # regeneration with identical content would stay "stale" forever if ( ninja_changed or not compdb.is_file() @@ -116,16 +110,14 @@ def run_compile(config: ConfigType, verbose: bool) -> int: cmd.append("-v") if jobs := config[CONF_ESPHOME].get(CONF_COMPILE_PROCESS_LIMIT): cmd += ["-j", str(jobs)] - # Explicit targets, not the default statement: a generator defect that - # drops them fails loudly with "unknown target" instead of a green - # no-op run that leaves stale artifacts in place + # Explicit targets: a generator defect that drops them fails loudly + # instead of a green no-op run leaving stale artifacts in place targets = ["firmware.factory.bin", "firmware.ota.bin"] cmd += targets - # A dry-run probe keeps a no-op rebuild quiet: ninja would only print - # "no work to do". A freshly rewritten manifest all but guarantees work, - # so skip the probe (and its full stat pass) on that path. cwd instead - # of -C also drops the "Entering directory" banner on real builds. + # The dry-run probe keeps a no-op rebuild quiet; a rewritten manifest + # all but guarantees work, so skip it then. cwd (not -C) drops the + # "Entering directory" banner. skip_build = False if not ninja_changed: probe = subprocess.run( @@ -138,9 +130,8 @@ def run_compile(config: ConfigType, verbose: bool) -> int: close_fds=False, ) if probe.stderr.strip(): - # A load-time diagnostic (e.g. "multiple rules generate X") - # flags a generator bug; the skip branch would otherwise - # swallow it forever + # A load-time diagnostic flags a generator bug; the skip branch + # would otherwise swallow it forever _LOGGER.warning("ninja: %s", probe.stderr.strip()) if probe.returncode != 0: # An unknown target here is the defective-manifest case; fall @@ -157,9 +148,8 @@ def run_compile(config: ConfigType, verbose: bool) -> int: if rc != 0: return rc - # ninja already refused a manifest missing the explicit targets above; - # existence covers the remaining hole (a rule that ran but wrote - # elsewhere). The factory/ota copies are what upload and OTA consume. + # ninja already refused missing targets; existence covers a rule that + # ran but wrote elsewhere build_dir_artifacts = ( get_elf_path(), build_dir / "firmware.bin", @@ -172,8 +162,7 @@ def run_compile(config: ConfigType, verbose: bool) -> int: return 1 if not _print_size_summary(build_dir, paths): - # The cause was already warned; name the consequence so a build - # contributing no RAM/Flash metric is visible to CI harnesses + # Cause already warned; name the consequence for CI harnesses _LOGGER.warning("Firmware size summary unavailable for this build") from esphome.build_helpers.idedata import warn_if_idedata_missing @@ -230,8 +219,7 @@ def _parse_app_size(build_dir: Path, paths: framework.InstalledPaths) -> int | N try: ld_text = ld_path.read_text(encoding="utf-8") except (OSError, UnicodeDecodeError) as err: - # UnicodeDecodeError: a truncated/corrupt script must degrade to - # the same warning, never abort an already-linked build + # A corrupt script degrades the same way, never aborts the build _LOGGER.warning("Cannot read linker script for the Flash summary: %s", err) return None app_size = segment_length(ld_text, "irom0_0_seg") @@ -288,9 +276,8 @@ def _print_size_summary(build_dir: Path, paths: framework.InstalledPaths) -> boo ", ".join(sorted(missing)), ) return False - # Resolve the flash budget before printing anything: a RAM line without - # its Flash line would let CI's memory-impact extraction sum the two - # metrics over different build counts (_parse_app_size already warned). + # Resolve the flash budget before printing: a RAM line without its + # Flash line would skew CI's memory-impact extraction app_size = _parse_app_size(build_dir, paths) if not app_size: return False @@ -314,8 +301,7 @@ def get_idedata(ccache: str | None = _CCACHE_UNRESOLVED) -> dict | None: from esphome.build_helpers.idedata import load_or_build_idedata if ccache is _CCACHE_UNRESOLVED: - # Deliberately uncached: env/PATH can change between builds in a - # long-lived host process + # Uncached: env/PATH can change between builds in a host process ccache = resolve_ccache_path() return load_or_build_idedata( get_build_dir() / "compile_commands.json", diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 928ed92578c..31ff7e4b7f8 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -127,9 +127,8 @@ def _validate_native_toolchain(config: ConfigType) -> ConfigType: "'toolchain: arduino' requires framework version " f"{MIN_FRAMEWORK_VERSION} or newer" ) - # platform_version is a PlatformIO concept; drop it (as esp32's native - # toolchain does), warning when a custom pin is discarded. The floor - # above guarantees the schema-derived default is the ARDUINO_4 spec. + # platform_version is a PlatformIO concept; drop it, warning when a + # custom pin is discarded if ( conf.pop(CONF_PLATFORM_VERSION, _ARDUINO_4_PLATFORM_SPEC) != _ARDUINO_4_PLATFORM_SPEC @@ -442,9 +441,8 @@ async def to_code(config: ConfigType) -> None: # Force-include inline std::__throw_* overrides so GCC dead-strips the unused # libstdc++ error message strings (e.g. "basic_string::_M_create") from DRAM. - # See throw_stubs.h for details. Must be prepended before , so this - # uses build_src_flags with -include. Unconditional: the native build - # generator reads the same option, keeping one source of truth. + # See throw_stubs.h. Unconditional: the native build generator reads + # the same option, keeping one source of truth. cg.add_platformio_option( "build_src_flags", "-include esphome/components/esp8266/throw_stubs.h" ) @@ -651,8 +649,7 @@ def _decode_pc(config: ConfigType, addr: str, *, bulk: bool = False) -> None: try: translation = subprocess.check_output(command, close_fds=False).decode().strip() except Exception as err: # noqa: BLE001 # pylint: disable=broad-except - # Warn, not debug: a failing addr2line must be visible. The warning - # is rate-limited across a dump, so mark every undecoded address + # Warn (rate-limited across a dump); mark undecoded addresses # inline or the rest read as merely unmappable if not _warn_decode_problem( "addr2line-failed", "Could not decode crash address %s (%s)", addr, err diff --git a/esphome/core/config.py b/esphome/core/config.py index 38bf4bd86c6..3b6688c84ae 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -555,8 +555,7 @@ NATIVE_ARDUINO_PIO_OPTIONS = frozenset({"board_build.f_cpu", "board_build.ldscri # that is stored rather than translated away. Consumed by the esp8266 native # backend (later in this chain) for its ignored-option warning; defined here # so it stays adjacent to the routing. -# build_src_flags and board_build.flash_mode: set unconditionally by -# esp8266/__init__ and read by the native generator; not user-routable, so +# build_src_flags and board_build.flash_mode are not user-routable, so # not in the set above NATIVE_ARDUINO_CONSUMED_PIO_OPTIONS = NATIVE_ARDUINO_PIO_OPTIONS | { "lib_ignore", diff --git a/esphome/writer.py b/esphome/writer.py index 543f1243bc0..3b5f69c4c53 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -616,10 +616,8 @@ def clean_build(clear_pio_cache: bool = True, *, full: bool = False): for name in PCH_ARTIFACT_NAMES: CORE.relative_build_path(name).unlink(missing_ok=True) - # The idedata caches are derived from the build but live under the data - # dir, not the build path, so they must be removed separately in both - # modes. Globbed (name.json plus name..json) so a future - # backend suffix cannot silently drift out of clean-all. + # idedata caches live under the data dir, not the build path; globbed + # so a future backend suffix cannot drift out of clean-all idedata_dir = CORE.relative_internal_path("idedata") for idedata_cache in ( *idedata_dir.glob(f"{CORE.name}.json"), diff --git a/script/determine-jobs.py b/script/determine-jobs.py index ed749643570..9eb6496541a 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -674,9 +674,8 @@ ESP8266_NATIVE_TEST_COMPONENTS = frozenset( } ) -# Infrastructure whose changes always trigger the native ESP8266 compile -# test. esphome/build_helpers/ holds the idedata and size-summary helpers -# the backend shares with the native ESP-IDF build. +# Infrastructure whose changes always trigger the native ESP8266 +# compile test ESP8266_NATIVE_TRIGGER_PATH_PREFIXES = ( "esphome/arduino8266/", "esphome/arduino/", diff --git a/script/test_build_components.py b/script/test_build_components.py index 95e9cd839f4..de93968d4c7 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -1076,17 +1076,15 @@ def test_components( pattern_components[pattern] = set(found) all_tests.update(found) - # The flag's contract is "no test matched fails": a fully blank pattern - # list would otherwise slide into the reference-baseline fallback and - # exit green while building nothing a caller asked for + # A blank pattern list would slide into the reference-baseline + # fallback and exit green while building nothing if fail_on_no_tests and not any(component_patterns): print("No components requested (blank component list)") return 1 if fail_on_no_tests and not all_tests: - # Nothing matched at all: fail before the synthetic baseline build, - # which would spend a compile reporting success on nothing. Partial - # matches defer to the per-pattern accounting after the summary. + # Nothing matched: fail before the synthetic baseline spends a + # compile reporting success on nothing print(f"No components found matching: {component_patterns}") return 1 @@ -1195,13 +1193,10 @@ def test_components( silent: list[str] = [] if fail_on_no_tests: - # A green run that built nothing for a requested pattern (renamed - # fixture, missing base file, version-suffix mismatch, a wildcard - # matching no component) must not pass CI. Per pattern: an - # all-or-nothing check would let one silent pattern hide behind the - # others. Opt-in: some legs (the esp32-ard smoke subset) - # legitimately match nothing. Failing is deferred past the summary - # so a real failure's reproduce commands still print. + # A green run that built nothing for a requested pattern must not + # pass CI. Per pattern so one silent pattern cannot hide behind + # the others; opt-in because some legs legitimately match nothing; + # deferred past the summary so reproduce commands still print. built = {c for r in test_results for c in r.components} # A pattern is silent when it matched no fixture, or when none of # its matched components produced a build (wildcards included)