mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 12:33:10 +00:00
[ci] Fix memory impact build selecting unbuildable platform (#16788)
This commit is contained in:
@@ -70,6 +70,7 @@ from helpers import (
|
||||
get_changed_components,
|
||||
get_component_from_path,
|
||||
get_component_test_files,
|
||||
get_component_test_platforms,
|
||||
get_components_with_dependencies,
|
||||
get_cpp_changed_components,
|
||||
get_fixture_to_test_files,
|
||||
@@ -77,7 +78,6 @@ from helpers import (
|
||||
get_target_branch,
|
||||
git_ls_files,
|
||||
is_validate_only_file,
|
||||
parse_test_filename,
|
||||
root_path,
|
||||
)
|
||||
from split_components_for_ci import create_intelligent_batches
|
||||
@@ -169,24 +169,6 @@ MEMORY_IMPACT_FALLBACK_COMPONENT = "api" # Representative component for core ch
|
||||
MEMORY_IMPACT_FALLBACK_PLATFORM = Platform.ESP32_IDF # Most representative platform
|
||||
MEMORY_IMPACT_MAX_COMPONENTS = 40 # Max components before results become nonsensical
|
||||
|
||||
# Platform-specific components that can only be built on their respective platforms
|
||||
# These components contain platform-specific code and cannot be cross-compiled
|
||||
# Regular components (wifi, logger, api, etc.) are cross-platform and not listed here
|
||||
PLATFORM_SPECIFIC_COMPONENTS = frozenset(
|
||||
{
|
||||
"esp32", # ESP32 platform implementation
|
||||
"esp8266", # ESP8266 platform implementation
|
||||
"rp2040", # Raspberry Pi Pico / RP2040 platform implementation
|
||||
"libretiny", # LibreTiny base platform implementation
|
||||
"bk72xx", # Beken BK72xx platform implementation (uses LibreTiny)
|
||||
"rtl87xx", # Realtek RTL87xx platform implementation (uses LibreTiny)
|
||||
"ln882x", # Winner Micro LN882x platform implementation (uses LibreTiny)
|
||||
"host", # Host platform (for testing on development machine)
|
||||
"nrf52", # Nordic nRF52 platform implementation (uses Zephyr)
|
||||
"zephyr", # Zephyr RTOS platform implementation
|
||||
}
|
||||
)
|
||||
|
||||
# Platform preference order for memory impact analysis
|
||||
# This order is used when no platform-specific hints are detected from filenames
|
||||
# Priority rationale:
|
||||
@@ -1006,23 +988,24 @@ def detect_memory_impact_config(
|
||||
] = {} # Track which platforms each component supports
|
||||
|
||||
for component in sorted(changed_component_set):
|
||||
# Look for test files on preferred platforms
|
||||
test_files = get_component_test_files(component, all_variants=True)
|
||||
if not test_files:
|
||||
continue
|
||||
|
||||
# Check if component has tests for any preferred platform
|
||||
available_platforms = [
|
||||
platform
|
||||
for test_file in test_files
|
||||
if (platform := parse_test_filename(test_file)[1]) != "all"
|
||||
and platform in MEMORY_IMPACT_PLATFORM_PREFERENCE
|
||||
]
|
||||
# Discover the platforms this component has BASE tests for, using the
|
||||
# same logic as the build runner (get_component_test_platforms wraps the
|
||||
# shared get_component_test_files + parse_test_filename helpers). Base
|
||||
# tests only: the memory impact CI build runs test_build_components.py
|
||||
# with --base-only, which compiles base test.<platform>.yaml files but
|
||||
# never variant test-<variant>.<platform>.yaml files. Counting
|
||||
# variant-only platforms here would let us select a platform the build
|
||||
# then has nothing to compile for, producing no memory output.
|
||||
available_platforms = {
|
||||
Platform(platform)
|
||||
for platform in get_component_test_platforms(component)
|
||||
if platform in MEMORY_IMPACT_PLATFORM_PREFERENCE
|
||||
}
|
||||
|
||||
if not available_platforms:
|
||||
continue
|
||||
|
||||
component_platforms_map[component] = set(available_platforms)
|
||||
component_platforms_map[component] = available_platforms
|
||||
components_with_tests.append(component)
|
||||
|
||||
# If no components have tests, don't run memory impact
|
||||
@@ -1084,20 +1067,57 @@ def detect_memory_impact_config(
|
||||
)
|
||||
platform = _select_platform_by_count(platform_counts)
|
||||
|
||||
# Filter out platform-specific components that are incompatible with selected platform
|
||||
# Platform components (esp32, esp8266, rp2040, etc.) can only build on their own platform
|
||||
# Other components (wifi, logger, etc.) are cross-platform and can build anywhere
|
||||
compatible_components = [
|
||||
component
|
||||
for component in components_with_tests
|
||||
if component not in PLATFORM_SPECIFIC_COMPONENTS
|
||||
or platform in component_platforms_map.get(component, set())
|
||||
]
|
||||
# Keep only components that have a base test on the selected platform.
|
||||
# The merged build runs test_build_components.py -t <platform> --base-only,
|
||||
# so a component without a base test.<platform>.yaml compiles nothing and
|
||||
# contributes no memory output. This also covers platform-specific
|
||||
# components (esp32, esp8266, etc.), which only have tests on their own
|
||||
# platform. When components don't share a common platform we build the
|
||||
# largest subset that does, dropping the rest.
|
||||
def components_supporting(target: Platform) -> list[str]:
|
||||
return [
|
||||
component
|
||||
for component in components_with_tests
|
||||
if target in component_platforms_map.get(component, set())
|
||||
]
|
||||
|
||||
# If no components are compatible with the selected platform, don't run
|
||||
compatible_components = components_supporting(platform)
|
||||
|
||||
# A platform hint (or no-common-platform fallback) can pick a platform that
|
||||
# no changed component actually has a base test for, leaving nothing to
|
||||
# build. In that case fall back to the platform supported by the most
|
||||
# components. component_platforms_map is non-empty (guarded above) and every
|
||||
# value is a non-empty platform set (components with no supported platform
|
||||
# are skipped at discovery), so this always yields a buildable platform with
|
||||
# at least one compatible component.
|
||||
if not compatible_components:
|
||||
platform = _select_platform_by_count(
|
||||
Counter(
|
||||
p for platforms in component_platforms_map.values() for p in platforms
|
||||
)
|
||||
)
|
||||
compatible_components = components_supporting(platform)
|
||||
|
||||
# Defensive backstop: unreachable given the invariant above, but guards
|
||||
# against a future regression in platform selection silently passing an
|
||||
# empty component list to the build.
|
||||
if not compatible_components:
|
||||
return {"should_run": "false"}
|
||||
|
||||
# Log components dropped because they lack a base test on the selected
|
||||
# platform so partial-subset builds are visible in CI logs.
|
||||
dropped_components = [
|
||||
component
|
||||
for component in components_with_tests
|
||||
if component not in compatible_components
|
||||
]
|
||||
if dropped_components:
|
||||
print(
|
||||
f"Memory impact: Dropping components without a base test on "
|
||||
f"{platform}: {dropped_components}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
# Debug output
|
||||
print("Memory impact analysis:", file=sys.stderr)
|
||||
print(f" Changed components: {sorted(changed_component_set)}", file=sys.stderr)
|
||||
|
||||
Reference in New Issue
Block a user