mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Add build-level tests verifying configdep selective rebuild behavior: - test_configdep.py: selective rebuild (only affected .obj rebuilt) and ELF string verification after Kconfig toggle - test_rebuild_configdep.py: full rebuild graph tests with configdep enabled, including the two-build "settle" pattern for .cdep stub timestamps Place the configdep test app in tools/test_build_system/configdep_test_app/ alongside the build system test infrastructure. Split configdep-specific rebuild coverage from pure CMake/Ninja rebuild tests (test_rebuild.py runs with configdep disabled via a module-local idf_py fixture).
109 lines
5.2 KiB
Python
109 lines
5.2 KiB
Python
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
"""Rebuild tests with esp-idf-configdep enabled (default idf.py behavior).
|
|
|
|
esp-idf-configdep rewrites compiler .d files to depend on per-option
|
|
build/config/.../*.cdep stubs instead of sdkconfig.h. When a referenced
|
|
.cdep file does not exist yet, the wrapper creates it **after** the real
|
|
compiler has written the .obj. Those stubs then have a newer mtime than the
|
|
object. On the **next** Ninja run, those .cdep paths are prerequisites in the
|
|
merged dependency graph, so Ninja correctly treats the object as out of date and
|
|
rebuilds it—even though nothing in the source tree or sdkconfig changed.
|
|
|
|
That one-time “settling” wave is not a CMake bug; it is an artifact of how
|
|
configdep materializes stubs relative to object timestamps. The tests in
|
|
test_rebuild.py therefore disable configdep to exercise pure CMake/Ninja
|
|
incremental behavior. Here we keep configdep **on** and run **two** full
|
|
idf.py build invocations before any mtime baseline so stub/object mtimes are
|
|
consistent; then we apply the same coarse-grained checks as the classic rebuild
|
|
tests (plus relaxed expectations where configdep legitimately skips compiling
|
|
TUs that do not reference changed CONFIG_* symbols).
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from test_build_system_helpers import ALL_ARTIFACTS_BUILDV1
|
|
from test_build_system_helpers import ALL_ARTIFACTS_BUILDV2
|
|
from test_build_system_helpers import APP_BINS
|
|
from test_build_system_helpers import BOOTLOADER_BINS
|
|
from test_build_system_helpers import BUILD_SNAPSHOT_EXCLUDE
|
|
from test_build_system_helpers import PARTITION_BIN
|
|
from test_build_system_helpers import IdfPyFunc
|
|
from test_build_system_helpers import get_snapshot
|
|
from test_build_system_helpers import rebuild_and_check
|
|
from test_build_system_helpers import replace_in_file
|
|
|
|
|
|
def _settle_configdep_graph(idf_py: IdfPyFunc) -> None:
|
|
"""Two full builds so ``.cdep`` stubs created by configdep are not newer than ``.obj`` for the next graph walk."""
|
|
logging.info('settle (configdep): first full build')
|
|
idf_py('build')
|
|
logging.info('settle (configdep): second full build reconciles obj vs .cdep mtimes')
|
|
idf_py('build')
|
|
|
|
|
|
@pytest.mark.usefixtures('test_app_copy')
|
|
def test_rebuild_no_changes_with_configdep(idf_py: IdfPyFunc, request: pytest.FixtureRequest) -> None:
|
|
"""No-op rebuild: with configdep on, baseline snapshot only after two settle builds (see module docstring)."""
|
|
_settle_configdep_graph(idf_py)
|
|
|
|
logging.info('baseline snapshot after settle')
|
|
all_build_files = get_snapshot('build/**/*', exclude_patterns=BUILD_SNAPSHOT_EXCLUDE)
|
|
|
|
logging.info('check that all build artifacts were generated')
|
|
all_artifacts = ALL_ARTIFACTS_BUILDV2 if request.config.getoption('buildv2', False) else ALL_ARTIFACTS_BUILDV1
|
|
for artifact in all_artifacts:
|
|
assert Path(artifact).exists()
|
|
|
|
logging.info('noop build with configdep; mtimes under baseline should match')
|
|
idf_py('build')
|
|
all_build_files_after = get_snapshot('build/**/*', exclude_patterns=BUILD_SNAPSHOT_EXCLUDE)
|
|
all_build_files_after.assert_same(all_build_files)
|
|
|
|
|
|
@pytest.mark.usefixtures('test_app_copy')
|
|
def test_rebuild_source_files_with_configdep(idf_py: IdfPyFunc) -> None:
|
|
"""Same selective-rebuild checks as ``test_rebuild_source_files``, after configdep settle builds."""
|
|
idf_path = Path(os.environ['IDF_PATH'])
|
|
_settle_configdep_graph(idf_py)
|
|
|
|
logging.info('updating a component source file rebuilds only that component')
|
|
component_files_patterns = [
|
|
'build/esp-idf/esp_system/libesp_system.a',
|
|
'build/esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/port/cpu_start.c.obj',
|
|
]
|
|
other_files_patterns = [
|
|
'build/esp-idf/lwip/liblwip.a',
|
|
'build/esp-idf/freertos/libfreertos.a',
|
|
*BOOTLOADER_BINS,
|
|
*PARTITION_BIN,
|
|
]
|
|
(idf_path / 'components/esp_system/port/cpu_start.c').touch()
|
|
rebuild_and_check(idf_py, component_files_patterns, other_files_patterns)
|
|
|
|
logging.info('changing a bootloader source file rebuilds the bootloader')
|
|
(idf_path / 'components/bootloader/subproject/main/bootloader_start.c').touch()
|
|
rebuild_and_check(idf_py, BOOTLOADER_BINS, APP_BINS + PARTITION_BIN)
|
|
|
|
logging.info('changing the partitions CSV file rebuilds only the partition table')
|
|
(idf_path / 'components/partition_table/partitions_singleapp.csv').touch()
|
|
rebuild_and_check(idf_py, PARTITION_BIN, APP_BINS + BOOTLOADER_BINS)
|
|
|
|
logging.info(
|
|
'sdkconfig update: binaries and generated headers must refresh (objects are selective under configdep)'
|
|
)
|
|
sdkconfig_files = ['build/config/sdkconfig.h', 'build/config/sdkconfig.json']
|
|
replace_in_file('sdkconfig', '# CONFIG_FREERTOS_UNICORE is not set', 'CONFIG_FREERTOS_UNICORE=y')
|
|
rebuild_and_check(idf_py, APP_BINS + BOOTLOADER_BINS + sdkconfig_files, PARTITION_BIN)
|
|
|
|
logging.info('Updating project CMakeLists.txt triggers app re-link at minimum')
|
|
replace_in_file(
|
|
'CMakeLists.txt',
|
|
'# placeholder_after_include_project_cmake',
|
|
'add_compile_options("-DUSELESS_MACRO_DOES_NOTHING=1")',
|
|
)
|
|
rebuild_and_check(idf_py, APP_BINS, PARTITION_BIN + BOOTLOADER_BINS + sdkconfig_files)
|