mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
f72292b0d7
The test_rebuild_no_changes test verifies that running idf.py build successively without any file changes results in identical build artifacts on the second run (i.e., nothing gets rebuilt). This test was failing in buildv2 because it expected kconfig_menus.json to be present in build/config/ after a normal build. However, in cmakev2, kconfig_menus.json is not generated during regular builds. In cmakev1, kconfig_menus.json was generated globally during every build alongside other config files (sdkconfig.h, sdkconfig.cmake, etc). In cmakev2, kconfig_menus.json generation does not happend for normal builds because it depends on the Kconfig menu hierarchy and cannot be generated globally. It must be generated per-executable. Hence, this commit updates the artefacts list for cmakev2 to not expect the kconfig_menus.json file during a build/re-build action.
167 lines
7.2 KiB
Python
167 lines
7.2 KiB
Python
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
# These tests check whether the build system rebuilds some files or not
|
|
# depending on the changes to the project.
|
|
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 PARTITION_BIN
|
|
from test_build_system_helpers import IdfPyFunc
|
|
from test_build_system_helpers import get_snapshot
|
|
from test_build_system_helpers import replace_in_file
|
|
|
|
|
|
@pytest.mark.usefixtures('test_app_copy')
|
|
def test_rebuild_no_changes(idf_py: IdfPyFunc, request: pytest.FixtureRequest) -> None:
|
|
logging.info('initial build')
|
|
idf_py('build')
|
|
logging.info('get the first snapshot')
|
|
# excluding the 'log' subdirectory here since it changes after every build
|
|
all_build_files = get_snapshot(
|
|
'build/**/*',
|
|
exclude_patterns=[
|
|
'build/log/*',
|
|
'build/CMakeFiles/bootloader-complete',
|
|
'build/bootloader-prefix/src/bootloader-stamp/bootloader-done',
|
|
],
|
|
)
|
|
|
|
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('build again with no changes')
|
|
idf_py('build')
|
|
# if there are no changes, nothing gets rebuilt
|
|
all_build_files_after_rebuild = get_snapshot(
|
|
'build/**/*',
|
|
exclude_patterns=[
|
|
'build/log/*',
|
|
'build/CMakeFiles/bootloader-complete',
|
|
'build/bootloader-prefix/src/bootloader-stamp/bootloader-done',
|
|
],
|
|
)
|
|
all_build_files_after_rebuild.assert_same(all_build_files)
|
|
|
|
|
|
def rebuild_and_check(
|
|
idf_py: IdfPyFunc, should_be_rebuilt: str | list[str], should_not_be_rebuilt: str | list[str]
|
|
) -> None:
|
|
"""
|
|
Helper function for the test cases below.
|
|
Asserts that the files matching 'should_be_rebuilt' patterns are rebuilt
|
|
and files matching 'should_not_be_rebuilt' patterns aren't, after
|
|
touching (updating the mtime) of the given 'file_to_touch' and rebuilding.
|
|
"""
|
|
snapshot_should_be_rebuilt = get_snapshot(should_be_rebuilt)
|
|
snapshot_should_not_be_rebuilt = get_snapshot(should_not_be_rebuilt)
|
|
idf_py('build')
|
|
snapshot_should_be_rebuilt.assert_different(get_snapshot(should_be_rebuilt))
|
|
snapshot_should_not_be_rebuilt.assert_same(get_snapshot(should_not_be_rebuilt))
|
|
|
|
|
|
# For this and the following test function, there are actually multiple logical
|
|
# tests in one test function. It would be better to have every check in a separate
|
|
# test case, but that would mean doing a full clean build each time. Having a few
|
|
# related checks per test function looks like a reasonable compromise.
|
|
# If the test function grows too big, try splitting it into two or more functions.
|
|
@pytest.mark.usefixtures('test_app_copy')
|
|
def test_rebuild_source_files(idf_py: IdfPyFunc) -> None:
|
|
idf_path = Path(os.environ['IDF_PATH'])
|
|
logging.info('initial build')
|
|
idf_py('build')
|
|
|
|
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 triggers full recompile')
|
|
# pick on .c, .cpp, .S file which includes sdkconfig.h:
|
|
obj_files = [
|
|
'build/esp-idf/esp_libc/CMakeFiles/__idf_esp_libc.dir/newlib_init.c.obj',
|
|
'build/esp-idf/nvs_flash/CMakeFiles/__idf_nvs_flash.dir/src/nvs_api.cpp.obj'
|
|
'build/esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/port/arch/xtensa/panic_handler_asm.S.obj',
|
|
]
|
|
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 + obj_files + sdkconfig_files, PARTITION_BIN)
|
|
|
|
logging.info('Updating project CMakeLists.txt triggers app recompile')
|
|
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 + obj_files, PARTITION_BIN + BOOTLOADER_BINS + sdkconfig_files)
|
|
|
|
|
|
@pytest.mark.usefixtures('test_app_copy')
|
|
def test_rebuild_linker(idf_py: IdfPyFunc) -> None:
|
|
idf_path = Path(os.environ['IDF_PATH'])
|
|
logging.info('initial build')
|
|
idf_py('build')
|
|
|
|
logging.info('Updating rom ld file should re-link app and bootloader')
|
|
(idf_path / 'components/esp_rom/esp32/ld/esp32.rom.ld').touch()
|
|
rebuild_and_check(idf_py, APP_BINS + BOOTLOADER_BINS, PARTITION_BIN)
|
|
|
|
logging.info('Updating app-only sections ld file should only re-link the app')
|
|
(idf_path / 'components/esp_system/ld/esp32/sections.ld.in').touch()
|
|
rebuild_and_check(idf_py, APP_BINS, BOOTLOADER_BINS + PARTITION_BIN)
|
|
|
|
logging.info('Updating app-only memory ld file should only re-link the app')
|
|
(idf_path / 'components/esp_system/ld/esp32/memory.ld.in').touch()
|
|
rebuild_and_check(idf_py, APP_BINS, BOOTLOADER_BINS + PARTITION_BIN)
|
|
|
|
logging.info('Updating fragment file should only re-link the app')
|
|
(idf_path / 'components/esp_common/common.lf').touch()
|
|
rebuild_and_check(idf_py, APP_BINS, BOOTLOADER_BINS + PARTITION_BIN)
|
|
|
|
|
|
@pytest.mark.usefixtures('idf_copy')
|
|
def test_rebuild_version_change(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
|
|
idf_path = Path(os.environ['IDF_PATH'])
|
|
|
|
logging.info('Creating version files')
|
|
version_idf_file = idf_path / 'version.txt'
|
|
version_idf_file.write_text('IDF_VER_0123456789_0123456789_0123456789')
|
|
|
|
version_tmp_file = test_app_copy / 'version.txt'
|
|
version_tmp_file.write_text('project-version-1.0')
|
|
|
|
logging.info('Build first version')
|
|
idf_py('build')
|
|
|
|
logging.info('Changing app version')
|
|
version_tmp_file.write_text('project-version-2.0(012345678901234567890123456789)')
|
|
|
|
logging.info('rebuild should update only app files, not bootloader')
|
|
rebuild_and_check(idf_py, APP_BINS, BOOTLOADER_BINS)
|
|
logging.info('new rebuild should not change anything')
|
|
rebuild_and_check(idf_py, [], APP_BINS + BOOTLOADER_BINS)
|