Merge branch 'task/buildv2_full_pipeline' into 'master'

Enable full buildv2 pipeline

Closes IDF-14180

See merge request espressif/esp-idf!49668
This commit is contained in:
Sudeep Mohanty
2026-07-03 11:28:57 +02:00
17 changed files with 435 additions and 68 deletions

View File

@@ -128,6 +128,77 @@ build_child_pipeline:
job: generate_build_child_pipeline job: generate_build_child_pipeline
strategy: depend strategy: depend
# Generates a separate child-pipeline YAML for the cmakev2 build system.
# The build_dir stays identical to cmakev1 (build_<target>_<config>); v1/v2
# binaries are kept separate in s3 by suffixing PIPELINE_COMMIT_SHA — see
# the python injection below — which idf-ci uses unmodified in its s3
# prefix (project/<sha>/...). Keeping build_dir identical preserves the
# upstream pytest matching that uses app.build_path to filter test cases.
generate_build_child_pipeline_buildv2:
extends:
- .build_template
- .rules:labels:buildv2
tags: [fast_run, shiny]
variables:
# The build child pipeline's app list is fixed here, at generation time.
# Set IDF_BUILD_V2 so that manifest `disable: if IDF_BUILD_V2 == "1"` rules
# take effect during app discovery; otherwise those apps (e.g. kasan_test,
# IDF-15864) are discovered, built and target-tested even though the build
# jobs run with IDF_BUILD_V2 injected by patch_buildv2_child_pipeline.py.
IDF_BUILD_V2: "1"
dependencies: # set dependencies to null to avoid missing artifacts issue
needs:
- pipeline_variables
- job: pre_check_complete
artifacts: false
- job: baseline_manifest_sha
optional: true
artifacts:
paths:
- buildv2_child_pipeline.yml
- test_related_apps.txt
- non_test_related_apps.txt
expire_in: 1 week
when: always
script:
- idf-ci --debug gitlab build-child-pipeline
-p components
-p examples
-p tools/test_apps
--modified-files "$MR_MODIFIED_FILES"
buildv2_child_pipeline.yml > idfci_buildv2_gen.log 2>&1
- tail -20 idfci_buildv2_gen.log
- shuf test_related_apps.txt -o test_related_apps.txt
- shuf non_test_related_apps.txt -o non_test_related_apps.txt
# Patch the generated yaml to activate the cmakev2 shim and route v2 build
# and target_test artifacts to a distinct s3 namespace. See the script
# docstring for the rationale.
- python tools/ci/dynamic_pipelines/scripts/patch_buildv2_child_pipeline.py buildv2_child_pipeline.yml
- 'echo "=== buildv2_child_pipeline.yml (post-injection) ==="; cat buildv2_child_pipeline.yml; echo "=== end ==="'
build_child_pipeline_buildv2:
stage: build
extends:
- .rules:labels:buildv2
needs:
- job: fast_build_check
optional: true
artifacts: false
- pipeline_variables
- generate_build_child_pipeline_buildv2
variables:
MR_MODIFIED_COMPONENTS: $MR_MODIFIED_COMPONENTS
MR_MODIFIED_FILES: $MR_MODIFIED_FILES
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
inherit:
variables: false
trigger:
include:
- artifact: buildv2_child_pipeline.yml
job: generate_build_child_pipeline_buildv2
strategy: depend
allow_failure: true
generate_prebuild_report: generate_prebuild_report:
extends: extends:
- .build_template - .build_template

View File

@@ -361,7 +361,7 @@ build_docker:
############################ ############################
# Host test with test apps # # Host test with test apps #
############################ ############################
test_pytest_qemu: .test_pytest_qemu_template:
extends: extends:
- .host_test_template - .host_test_template
artifacts: artifacts:
@@ -371,15 +371,6 @@ test_pytest_qemu:
- "**/build*/*.bin" - "**/build*/*.bin"
reports: reports:
junit: XUNIT_RESULT.xml junit: XUNIT_RESULT.xml
parallel:
matrix:
- IDF_TARGET: "esp32"
INSTALL_EXTRA_TOOLS: "qemu-xtensa"
# Skip Clang + Xtensa tests due to bootloader size issue
IDF_TOOLCHAIN: [gcc]
- IDF_TARGET: "esp32c3"
INSTALL_EXTRA_TOOLS: "qemu-riscv32"
IDF_TOOLCHAIN: [gcc, clang]
script: script:
- run_cmd idf-ci build run - run_cmd idf-ci build run
--build-system cmake --build-system cmake
@@ -394,7 +385,20 @@ test_pytest_qemu:
--ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
--qemu-extra-args \"-global driver=timer.$IDF_TARGET.timg,property=wdt_disable,value=true\" --qemu-extra-args \"-global driver=timer.$IDF_TARGET.timg,property=wdt_disable,value=true\"
test_pytest_linux: test_pytest_qemu:
extends:
- .test_pytest_qemu_template
parallel:
matrix:
- IDF_TARGET: "esp32"
INSTALL_EXTRA_TOOLS: "qemu-xtensa"
# Skip Clang + Xtensa tests due to bootloader size issue
IDF_TOOLCHAIN: [gcc]
- IDF_TARGET: "esp32c3"
INSTALL_EXTRA_TOOLS: "qemu-riscv32"
IDF_TOOLCHAIN: [gcc, clang]
.test_pytest_linux_template:
extends: extends:
- .host_test_template - .host_test_template
artifacts: artifacts:
@@ -418,6 +422,10 @@ test_pytest_linux:
--junitxml=XUNIT_RESULT.xml --junitxml=XUNIT_RESULT.xml
--ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
test_pytest_linux:
extends:
- .test_pytest_linux_template
test_pytest_macos: test_pytest_macos:
extends: extends:
- .host_test_template - .host_test_template
@@ -524,6 +532,34 @@ pytest_buildv2_system:
--junitxml ${CI_PROJECT_DIR}/XUNIT_RESULT.xml --junitxml ${CI_PROJECT_DIR}/XUNIT_RESULT.xml
--ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
# Run QEMU tests with the cmakev2 build system (via the IDF_BUILD_V2 shim).
# Triggered only when the 'buildv2' MR label is set.
test_pytest_qemu_buildv2:
extends:
- .test_pytest_qemu_template
- .rules:labels:buildv2
allow_failure: true
parallel:
matrix:
- IDF_TARGET: "esp32"
INSTALL_EXTRA_TOOLS: "qemu-xtensa"
IDF_TOOLCHAIN: [gcc]
- IDF_TARGET: "esp32c3"
INSTALL_EXTRA_TOOLS: "qemu-riscv32"
IDF_TOOLCHAIN: [gcc]
variables:
IDF_BUILD_V2: "1"
# Run Linux-target pytest tests with the cmakev2 build system (via the
# IDF_BUILD_V2 shim). Triggered only when the 'buildv2' MR label is set.
test_pytest_linux_buildv2:
extends:
- .test_pytest_linux_template
- .rules:labels:buildv2
allow_failure: true
variables:
IDF_BUILD_V2: "1"
pytest_build_system_macos: pytest_build_system_macos:
extends: extends:
- .test_build_system_template - .test_build_system_template

View File

@@ -19,7 +19,10 @@ ignore_warning_files = [
'$IDF_PATH/tools/ci/ignore_build_warnings.txt', '$IDF_PATH/tools/ci/ignore_build_warnings.txt',
] ]
build_dir = "build_@t_@w" # BUILDV2_DIR_SUFFIX is expanded by idf-build-apps from the environment;
# empty for buildv1 builds and set to "_v2" by the buildv2 CI pipelines so
# buildv1 and buildv2 build artifacts do not share the same s3 cache path.
build_dir = "build${BUILDV2_DIR_SUFFIX}_@t_@w"
build_log_filename = "build_log.txt" build_log_filename = "build_log.txt"
size_json_filename = "size_${CI_JOB_ID}.json" size_json_filename = "size_${CI_JOB_ID}.json"

View File

@@ -27,10 +27,29 @@ if(EXISTS ${PROJECT_EXTRA_COMPONENTS})
endif() endif()
if(IGNORE_EXTRA_COMPONENT) if(IGNORE_EXTRA_COMPONENT)
# Prefix all entries of the list with ${PROJECT_EXTRA_COMPONENTS} absolute path foreach(extra_component_dir ${EXTRA_COMPONENT_DIRS})
list(TRANSFORM IGNORE_EXTRA_COMPONENT # Strip trailing slash so get_filename_component(NAME) returns the
PREPEND "${PROJECT_EXTRA_COMPONENTS}/" # component name rather than an empty string.
OUTPUT_VARIABLE EXTRA_COMPONENT_EXCLUDE_DIRS) string(REGEX REPLACE "/+$" "" extra_component_dir "${extra_component_dir}")
if(EXISTS "${extra_component_dir}/CMakeLists.txt")
# BOOTLOADER_EXTRA_COMPONENT_DIRS may point directly to a single component.
get_filename_component(component "${extra_component_dir}" NAME)
if(component AND ${component} IN_LIST IGNORE_EXTRA_COMPONENT)
list(APPEND EXTRA_COMPONENT_EXCLUDE_DIRS "${extra_component_dir}")
endif()
else()
# BOOTLOADER_EXTRA_COMPONENT_DIRS may also point to a directory containing
# multiple component directories.
file(GLOB proj_components RELATIVE ${extra_component_dir} ${extra_component_dir}/*)
foreach(component ${proj_components})
# Only directories are considered components.
if(IS_DIRECTORY "${extra_component_dir}/${component}"
AND ${component} IN_LIST IGNORE_EXTRA_COMPONENT)
list(APPEND EXTRA_COMPONENT_EXCLUDE_DIRS "${extra_component_dir}/${component}")
endif()
endforeach()
endif()
endforeach()
endif() endif()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@@ -1,3 +1,5 @@
CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n CONFIG_ESP_TASK_WDT_EN=n
CONFIG_IDF_EXPERIMENTAL_FEATURES=y CONFIG_IDF_EXPERIMENTAL_FEATURES=y
# CONFIG_VFS_SUPPORT_IO resolves to its default (y) in this test's Kconfig closure; the resulting console I/O reduces the internal DMA heap and fails the 200 KB allocation in the "GDMA M2M Weighted Arbitration Test SRAM->SRAM" case. Disable it to free that heap.
CONFIG_VFS_SUPPORT_IO=n

View File

@@ -13,3 +13,6 @@ CONFIG_PARTITION_TABLE_OFFSET=0x9000
# HMAC opaque driver test # HMAC opaque driver test
CONFIG_MBEDTLS_TEST_HMAC_OPAQUE_EFUSE_KEY=y CONFIG_MBEDTLS_TEST_HMAC_OPAQUE_EFUSE_KEY=y
CONFIG_MBEDTLS_TEST_HMAC_OPAQUE_EFUSE_KEY_ID=0 CONFIG_MBEDTLS_TEST_HMAC_OPAQUE_EFUSE_KEY_ID=0
# CONFIG_ESP_TLS_USE_DS_PERIPHERAL resolves to its default (y) in this test's Kconfig closure, which fails the test_mbedtls_hmac_opaque test on esp32c3. Disable it to pass; root cause still under investigation. TODO: IDF-15856
CONFIG_ESP_TLS_USE_DS_PERIPHERAL=n

View File

@@ -5,3 +5,6 @@ CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
# Now the runners are massively using xmc-c chips, to be removed when xmc-d goes massive production. # Now the runners are massively using xmc-c chips, to be removed when xmc-d goes massive production.
CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND=y CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND=y
# The options below resolve to their defaults (enabled) in this test's Kconfig closure, which fails the test_flash_auto_suspend_generic test on esp32c5 and esp32c61. Disable them to pass; root cause still under investigation. TODO: IDF-15858
CONFIG_VFS_SUPPORT_IO=n
CONFIG_ESP_WIFI_SLP_IRAM_OPT=n

View File

@@ -25,44 +25,68 @@ except ImportError:
from gcov_capture import get_coverage_data from gcov_capture import get_coverage_data
def _candidate_target_dirs(binary_path: str, component: str) -> list:
"""Per-component CMakeFiles/<target>.dir/ directories used by either build system.
Build system v1 emits ``__idf_<component>.dir`` (double underscore); Build system
v2 emits ``_idf_<component>.dir`` (single underscore). Both candidates are returned
so the caller can probe either layout without knowing which build system produced
the binary.
"""
parent = os.path.join(binary_path, 'esp-idf', component, 'CMakeFiles')
return [
os.path.join(parent, f'__idf_{component}.dir'),
os.path.join(parent, f'_idf_{component}.dir'),
]
def _resolve_gcda_path(binary_path: str, component: str, basename: str) -> str:
"""Return the .gcda path, preferring whichever build-system layout already has the file.
Before the chip dumps, neither layout has a .gcda yet, so the chip-side FOPEN will
create the directory of whichever path the binary embedded. Call this again after
the dump to get the materialized path.
"""
for d in _candidate_target_dirs(binary_path, component):
candidate = os.path.join(d, basename)
if os.path.isfile(candidate):
return candidate
# Default to v2 layout; the post-dump call will return the actual path.
return os.path.join(_candidate_target_dirs(binary_path, component)[1], basename)
def get_expected_gcda_paths(dut: IdfDut) -> list: def get_expected_gcda_paths(dut: IdfDut) -> list:
"""Get list of expected .gcda file paths for this example.""" """Get list of expected .gcda file paths for this example."""
return [ return [
os.path.join( _resolve_gcda_path(dut.app.binary_path, 'main', 'gcov_example_main.c.gcda'),
dut.app.binary_path, 'esp-idf', 'main', 'CMakeFiles', '__idf_main.dir', 'gcov_example_main.c.gcda' _resolve_gcda_path(dut.app.binary_path, 'main', 'gcov_example_func.c.gcda'),
), _resolve_gcda_path(dut.app.binary_path, 'sample', 'some_funcs.c.gcda'),
os.path.join(
dut.app.binary_path, 'esp-idf', 'main', 'CMakeFiles', '__idf_main.dir', 'gcov_example_func.c.gcda'
),
os.path.join(dut.app.binary_path, 'esp-idf', 'sample', 'CMakeFiles', '__idf_sample.dir', 'some_funcs.c.gcda'),
] ]
def prepare_test(dut: IdfDut) -> list: def prepare_test(dut: IdfDut) -> list:
"""Prepare test environment: create directories and clean up old .gcda files. """Prepare test environment: create both candidate directories and clean stale .gcda."""
# Pre-create both build-system layouts so the chip-side FOPEN can write to whichever
Returns list of expected .gcda file paths. # the binary embedded, and clean stale .gcda from either layout.
""" for component in ('main', 'sample'):
# Create the generated .gcda folders for d in _candidate_target_dirs(dut.app.binary_path, component):
# Normally created via `idf.py build`, but in CI non-related files aren't preserved os.makedirs(d, exist_ok=True)
os.makedirs(os.path.join(dut.app.binary_path, 'esp-idf', 'main', 'CMakeFiles', '__idf_main.dir'), exist_ok=True)
os.makedirs(os.path.join(dut.app.binary_path, 'esp-idf', 'sample', 'CMakeFiles', '__idf_sample.dir'), exist_ok=True)
# Get expected paths and clean up old files
expected_gcda_paths = get_expected_gcda_paths(dut)
for gcda_path in expected_gcda_paths:
if os.path.isfile(gcda_path):
try: try:
os.remove(gcda_path) for entry in os.listdir(d):
print(f'Removed old .gcda file: {os.path.basename(gcda_path)}') if entry.endswith('.gcda'):
except OSError as e: try:
print(f'Warning: Could not remove {gcda_path}: {e}') os.remove(os.path.join(d, entry))
print(f'Removed old .gcda file: {entry}')
except OSError as e:
print(f'Warning: Could not remove {os.path.join(d, entry)}: {e}')
except OSError:
pass
return expected_gcda_paths return get_expected_gcda_paths(dut)
def _test_gcov(openocd_dut: 'OpenOCD', dut: IdfDut) -> None: def _test_gcov(openocd_dut: 'OpenOCD', dut: IdfDut) -> None:
expected_gcda_paths = prepare_test(dut) prepare_test(dut)
def expect_counter_output(loop: int, timeout: int = 10) -> None: def expect_counter_output(loop: int, timeout: int = 10) -> None:
dut.expect_exact( dut.expect_exact(
@@ -95,6 +119,8 @@ def _test_gcov(openocd_dut: 'OpenOCD', dut: IdfDut) -> None:
# Verify execution counts if expected and coverage data is available # Verify execution counts if expected and coverage data is available
if expected_counts: if expected_counts:
# Re-resolve gcda paths after the dump so we observe the layout the chip actually used.
expected_gcda_paths = get_expected_gcda_paths(dut)
coverage = get_coverage_data(dut.app.binary_path, expected_gcda_paths) coverage = get_coverage_data(dut.app.binary_path, expected_gcda_paths)
if coverage: # Only verify if detailed data is available if coverage: # Only verify if detailed data is available
print(f'Coverage data: {coverage}') print(f'Coverage data: {coverage}')
@@ -161,7 +187,7 @@ def _test_gcov_uart(dut: IdfDut) -> None:
# Close console port to free up the port for the gcov capture # Close console port to free up the port for the gcov capture
dut.serial.close() dut.serial.close()
expected_gcda_paths = prepare_test(dut) prepare_test(dut)
log_file = os.path.join(dut.logdir, 'gcov_uart.log') log_file = os.path.join(dut.logdir, 'gcov_uart.log')
uart_port = dut.serial.port uart_port = dut.serial.port
baud = dut.app.sdkconfig.get('APPTRACE_UART_BAUDRATE') baud = dut.app.sdkconfig.get('APPTRACE_UART_BAUDRATE')
@@ -178,6 +204,8 @@ def _test_gcov_uart(dut: IdfDut) -> None:
# Verify each dump # Verify each dump
for dump_num, expected in enumerate(expected_counts, start=1): for dump_num, expected in enumerate(expected_counts, start=1):
if uart_capture.wait_for_fstop(timeout=10.0): if uart_capture.wait_for_fstop(timeout=10.0):
# Re-resolve gcda paths after the dump so we observe the layout the chip actually used.
expected_gcda_paths = get_expected_gcda_paths(dut)
coverage = get_coverage_data(dut.app.binary_path, expected_gcda_paths) coverage = get_coverage_data(dut.app.binary_path, expected_gcda_paths)
if coverage: # Only verify details if coverage data is available if coverage: # Only verify details if coverage data is available

View File

@@ -0,0 +1,78 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
"""Patch the generated Build system v2 build child pipeline in place.
The buildv2 build child pipeline is generated by ``idf-ci gitlab build-child-pipeline``
from the same manifest as the default pipeline. This script post-processes that
generated YAML so the cmakev2 path is exercised end to end:
1. Inject ``IDF_BUILD_V2`` into each child build job so cmake activates the
cmakev2 shim.
2. Inject ``PIPELINE_COMMIT_SHA: ${PIPELINE_COMMIT_SHA}_buildv2`` into each child
build job. idf-ci reads ``PIPELINE_COMMIT_SHA`` when computing the s3
upload/download prefix (``project/<sha>/...``). Suffixing it for v2 jobs only
routes v2 binaries to a distinct s3 key namespace without renaming build_dir.
3. Redefine ``generate_pytest_child_pipeline`` so its script also runs
``patch_buildv2_target_test_pipeline.py`` against the emitted
``target_test_child_pipeline.yml`` and injects the same
``PIPELINE_COMMIT_SHA`` override into each target_test job. Without this, the
v2 target_test pipeline would still download from the default pipeline's s3
keys (race-determined, mostly cmakev1 binaries).
"""
import argparse
import yaml
PIPELINE_COMMIT_SHA_V2 = '${PIPELINE_COMMIT_SHA}_buildv2'
def patch(path: str) -> None:
with open(path) as f:
d = yaml.safe_load(f)
injected = []
for k, v in d.items():
if isinstance(v, dict) and 'extends' in v:
v.setdefault('variables', {})['IDF_BUILD_V2'] = '1'
v['variables']['PIPELINE_COMMIT_SHA'] = PIPELINE_COMMIT_SHA_V2
injected.append(k)
# Redefine generate_pytest_child_pipeline (included from
# tools/ci/dynamic_pipelines/templates/test_child_pipeline.yml) so its script
# also rewrites target_test_child_pipeline.yml. GitLab CI merges values at the
# keyword level, so only `script` is overridden; `needs`, `artifacts`,
# `image`, etc. come from the included version.
d['generate_pytest_child_pipeline'] = {
# Set IDF_BUILD_V2 so test-case collection honors `if IDF_BUILD_V2 == "1"`
'variables': {'IDF_BUILD_V2': '1'},
'script': [
'python tools/ci/dynamic_pipelines/scripts/generate_target_test_child_pipeline.py',
'python tools/ci/dynamic_pipelines/scripts/patch_buildv2_target_test_pipeline.py '
'target_test_child_pipeline.yml',
],
}
with open(path, 'w') as f:
yaml.safe_dump(d, f, sort_keys=False)
print('Injected IDF_BUILD_V2 + PIPELINE_COMMIT_SHA into build jobs:', injected)
print('Redefined generate_pytest_child_pipeline with post-processing step')
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'pipeline_yaml',
nargs='?',
default='buildv2_child_pipeline.yml',
help='Path to the generated buildv2 build child pipeline YAML to patch in place.',
)
args = parser.parse_args()
patch(args.pipeline_yaml)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,56 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
"""Patch the generated Build system v2 target_test child pipeline in place.
Inject ``PIPELINE_COMMIT_SHA: ${PIPELINE_COMMIT_SHA}_buildv2`` into every
target_test job so idf-ci downloads the cmakev2 binaries from the distinct s3
key namespace produced by the v2 build jobs, instead of racing with the default
pipeline's binaries.
Run from generate_pytest_child_pipeline (see patch_buildv2_child_pipeline.py),
after generate_target_test_child_pipeline.py has emitted the YAML.
"""
import argparse
import yaml
PIPELINE_COMMIT_SHA_V2 = '${PIPELINE_COMMIT_SHA}_buildv2'
# Target test jobs use extends: .dynamic_target_test_template, so their script:
# comes from the template, not their own definition. These top-level keys are
# pipeline configuration rather than jobs and must be skipped.
SKIP_KEYS = {'include', 'workflow', 'default', 'variables', 'stages'}
def patch(path: str) -> None:
with open(path) as f:
d = yaml.safe_load(f)
modified = 0
for k, v in d.items():
if k in SKIP_KEYS or not isinstance(v, dict):
continue
v.setdefault('variables', {})['PIPELINE_COMMIT_SHA'] = PIPELINE_COMMIT_SHA_V2
modified += 1
with open(path, 'w') as f:
yaml.safe_dump(d, f, sort_keys=False)
print(f'Injected PIPELINE_COMMIT_SHA override into {modified} target_test jobs')
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'pipeline_yaml',
nargs='?',
default='target_test_child_pipeline.yml',
help='Path to the generated target_test child pipeline YAML to patch in place.',
)
args = parser.parse_args()
patch(args.pipeline_yaml)
if __name__ == '__main__':
main()

View File

@@ -24,6 +24,7 @@ warning: unknown kconfig symbol 'CTRL_BTDM_MODEM_SLEEP' assigned to*
warning: unknown kconfig symbol 'ESP_DEFAULT_CPU_FREQ_MHZ_80' assigned to 'y' in .*/examples/system/deep_sleep/* warning: unknown kconfig symbol 'ESP_DEFAULT_CPU_FREQ_MHZ_80' assigned to 'y' in .*/examples/system/deep_sleep/*
warning: unknown kconfig symbol 'ESP_DEFAULT_CPU_FREQ_MHZ_80' assigned to 'y' in .*/examples/system/light_sleep/* warning: unknown kconfig symbol 'ESP_DEFAULT_CPU_FREQ_MHZ_80' assigned to 'y' in .*/examples/system/light_sleep/*
warning: unknown kconfig symbol 'ESP_DEFAULT_CPU_FREQ_MHZ_80' assigned to 'y' in .*/examples/wifi/power_save/* warning: unknown kconfig symbol 'ESP_DEFAULT_CPU_FREQ_MHZ_80' assigned to 'y' in .*/examples/wifi/power_save/*
warning: unknown kconfig symbol 'ESP_WIFI_SLP_IRAM_OPT' assigned to 'n' in .*/components/spi_flash/test_apps/flash_suspend/*
warning: unknown kconfig symbol 'ESP32_REV_MIN_3' assigned to 'y' in .*/examples/system/ota/simple_ota_example/sdkconfig.ci.on_update_no_sb_rsa warning: unknown kconfig symbol 'ESP32_REV_MIN_3' assigned to 'y' in .*/examples/system/ota/simple_ota_example/sdkconfig.ci.on_update_no_sb_rsa
warning: unknown kconfig symbol 'ESP32_REV_MIN' assigned to '3' in .*/examples/system/ota/simple_ota_example/sdkconfig.ci.on_update_no_sb_rsa warning: unknown kconfig symbol 'ESP32_REV_MIN' assigned to '3' in .*/examples/system/ota/simple_ota_example/sdkconfig.ci.on_update_no_sb_rsa
warning: unknown kconfig symbol 'ESP32H4_RTC_CLK_CAL_CYCLES' assigned to '576' in .*/examples/bluetooth/nimble/blecent/sdkconfig.defaults.esp32h4 warning: unknown kconfig symbol 'ESP32H4_RTC_CLK_CAL_CYCLES' assigned to '576' in .*/examples/bluetooth/nimble/blecent/sdkconfig.defaults.esp32h4
@@ -49,3 +50,8 @@ warning: unknown kconfig symbol 'ETHERNET_PHY_RST_GPIO' assigned to '5' in .*/ex
warning: unknown kconfig symbol 'ETHERNET_PHY_ADDR' assigned to '1' in .*/examples/protocols/esp_http_client/sdkconfig.ci.* warning: unknown kconfig symbol 'ETHERNET_PHY_ADDR' assigned to '1' in .*/examples/protocols/esp_http_client/sdkconfig.ci.*
warning: \S+ changed binding to STB_WEAK warning: \S+ changed binding to STB_WEAK
warning: unknown kconfig symbol 'VFS_SUPPORT_IO' assigned to 'n' in .*/tools/test_apps/system/g1_components/sdkconfig.defaults warning: unknown kconfig symbol 'VFS_SUPPORT_IO' assigned to 'n' in .*/tools/test_apps/system/g1_components/sdkconfig.defaults
warning: unknown kconfig symbol 'VFS_SUPPORT_IO' assigned to 'n' in .*/components/esp_driver_dma/test_apps/dma/sdkconfig\S*
warning: unknown kconfig symbol 'VFS_SUPPORT_IO' assigned to 'n' in .*/tools/test_apps/system/panic/coredump/sdkconfig\S*
warning: unknown kconfig symbol 'ESP_WIFI_TASK_PINNED_TO_CORE_1' assigned to 'y' in .*/tools/test_apps/system/esp_intr_dump/sdkconfig\S*
warning: unknown kconfig symbol 'VFS_SUPPORT_IO' assigned to 'n' in .*/components/spi_flash/test_apps/flash_suspend/sdkconfig\S*
warning: unknown kconfig symbol 'ESP_TLS_USE_DS_PERIPHERAL' assigned to 'n' in .*/components/mbedtls/test_apps/mbedtls_ut/sdkconfig\S*

View File

@@ -38,8 +38,9 @@ function(__generate_gdbinit)
set(symbols_gdbinit_path ${gdbinit_dir}/symbols) set(symbols_gdbinit_path ${gdbinit_dir}/symbols)
set(py_extensions_gdbinit_path ${gdbinit_dir}/py_extensions) set(py_extensions_gdbinit_path ${gdbinit_dir}/py_extensions)
set(connect_gdbinit_path ${gdbinit_dir}/connect) set(connect_gdbinit_path ${gdbinit_dir}/connect)
idf_build_get_property(PROJECT_EXECUTABLE EXECUTABLE) # EXECUTABLE is a CMake target name; the on-disk elf is EXECUTABLE_NAME + ".elf".
set(application_elf ${BUILD_DIR}/${PROJECT_EXECUTABLE}) idf_build_get_property(PROJECT_EXECUTABLE_NAME EXECUTABLE_NAME)
set(application_elf ${BUILD_DIR}/${PROJECT_EXECUTABLE_NAME}.elf)
file(MAKE_DIRECTORY ${gdbinit_dir}) file(MAKE_DIRECTORY ${gdbinit_dir})

View File

@@ -36,35 +36,21 @@ set(MENUCONFIG_INLINE_MIN_KCONFIG_VERSION "3.9.0")
function(__init_kconfig) function(__init_kconfig)
idf_build_get_property(idf_path IDF_PATH) idf_build_get_property(idf_path IDF_PATH)
# Initialize SDKCONFIG and SDKCONFIG_DEFAULTS build properties using environment # Initialize the SDKCONFIG file path build property.
# variables, CMake cache variables, or default values.
if(EXISTS "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
set(sdkconfig_defaults "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
else()
set(sdkconfig_defaults "")
endif()
__get_default_value(VARIABLE SDKCONFIG __get_default_value(VARIABLE SDKCONFIG
DEFAULT "${CMAKE_SOURCE_DIR}/sdkconfig" DEFAULT "${CMAKE_SOURCE_DIR}/sdkconfig"
OUTPUT sdkconfig) OUTPUT sdkconfig)
__get_default_value(VARIABLE SDKCONFIG_DEFAULTS
DEFAULT "${sdkconfig_defaults}"
OUTPUT sdkconfig_defaults)
__get_absolute_paths(PATHS "${sdkconfig}" OUTPUT sdkconfig) __get_absolute_paths(PATHS "${sdkconfig}" OUTPUT sdkconfig)
__get_absolute_paths(PATHS "${sdkconfig_defaults}" OUTPUT sdkconfig_defaults)
set(sdkconfig_defaults_checked "")
foreach(sdkconfig_default ${sdkconfig_defaults})
if(NOT EXISTS "${sdkconfig_default}")
idf_die("SDKCONFIG_DEFAULTS '${sdkconfig_default}' does not exist.")
endif()
list(APPEND sdkconfig_defaults_checked ${sdkconfig_default})
endforeach()
idf_build_set_property(SDKCONFIG "${sdkconfig}") idf_build_set_property(SDKCONFIG "${sdkconfig}")
idf_build_set_property(__SDKCONFIG_ORIG "${sdkconfig}") idf_build_set_property(__SDKCONFIG_ORIG "${sdkconfig}")
idf_build_set_property(SDKCONFIG_DEFAULTS "${sdkconfig_defaults_checked}")
# Initialize the SDKCONFIG_DEFAULTS build property. __init_idf_target()
# reads this property to detect the target from the defaults files, so it
# must be resolved here. It is resolved again from __generate_sdkconfig()
# to pick up a SDKCONFIG_DEFAULTS variable assigned after
# include(project.cmake) but before project().
__resolve_sdkconfig_defaults()
idf_build_set_property(GENERATE_SDKCONFIG 1) idf_build_set_property(GENERATE_SDKCONFIG 1)
# Setup ESP-IDF root Kconfig and sdkconfig.rename files. # Setup ESP-IDF root Kconfig and sdkconfig.rename files.
@@ -78,6 +64,54 @@ function(__init_kconfig)
idf_build_set_property(CONFIG_DIR "${config_dir}") idf_build_set_property(CONFIG_DIR "${config_dir}")
endfunction() endfunction()
#[[
__resolve_sdkconfig_defaults()
Resolve SDKCONFIG_DEFAULTS into a list of absolute, existing paths
and store it on the SDKCONFIG_DEFAULTS build property.
A SDKCONFIG_DEFAULTS variable (or environment variable) takes precedence
and is (re)resolved onto the build property. Otherwise a value already
accumulated on the build property (e.g. via
``idf_build_set_property(SDKCONFIG_DEFAULTS "<path>" APPEND)``) is
preserved, and only when nothing has been set does it fall back to the
project's sdkconfig.defaults file. This makes the helper idempotent and
safe to call both early (from __init_kconfig) and again at
sdkconfig-generation time.
#]]
function(__resolve_sdkconfig_defaults)
if(NOT DEFINED SDKCONFIG_DEFAULTS AND NOT DEFINED ENV{SDKCONFIG_DEFAULTS})
# No SDKCONFIG_DEFAULTS variable override: keep a value already set on
# the build property; only fall back to the default when unset.
idf_build_get_property(existing SDKCONFIG_DEFAULTS)
if(existing)
return()
endif()
endif()
if(EXISTS "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
set(sdkconfig_defaults_default "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
else()
set(sdkconfig_defaults_default "")
endif()
__get_default_value(VARIABLE SDKCONFIG_DEFAULTS
DEFAULT "${sdkconfig_defaults_default}"
OUTPUT sdkconfig_defaults)
__get_absolute_paths(PATHS "${sdkconfig_defaults}" OUTPUT sdkconfig_defaults)
set(sdkconfig_defaults_checked "")
foreach(sdkconfig_default IN LISTS sdkconfig_defaults)
if(NOT EXISTS "${sdkconfig_default}")
idf_die("SDKCONFIG_DEFAULTS '${sdkconfig_default}' does not exist.")
endif()
list(APPEND sdkconfig_defaults_checked "${sdkconfig_default}")
endforeach()
idf_build_set_property(SDKCONFIG_DEFAULTS "${sdkconfig_defaults_checked}")
endfunction()
#[[ #[[
__create_sdkconfig_orig_copy() __create_sdkconfig_orig_copy()
@@ -187,6 +221,13 @@ endfunction()
4. Generate all output files (sdkconfig.h, sdkconfig.cmake, etc.) 4. Generate all output files (sdkconfig.h, sdkconfig.cmake, etc.)
#]] #]]
function(__generate_sdkconfig) function(__generate_sdkconfig)
# Re-resolve SDKCONFIG_DEFAULTS so that a value assigned to the
# SDKCONFIG_DEFAULTS variable after include(project.cmake) but before
# project() is honored. The helper is idempotent: it only re-reads when
# the SDKCONFIG_DEFAULTS variable (or environment variable) is set, and
# otherwise leaves a value accumulated on the build property untouched.
__resolve_sdkconfig_defaults()
# Collect Kconfig files from discovered components # Collect Kconfig files from discovered components
__consolidate_component_kconfig_files() __consolidate_component_kconfig_files()

View File

@@ -106,6 +106,9 @@ tools/test_apps/system/init_array:
- tools/tools.json - tools/tools.json
tools/test_apps/system/kasan_test: tools/test_apps/system/kasan_test:
disable:
- if: IDF_BUILD_V2 == "1"
reason: 'KASAN is not yet supported under build system v2. TODO: IDF-15864'
depends_components: depends_components:
- *common_components - *common_components
- esp_system - esp_system

View File

@@ -0,0 +1,2 @@
# The Wi-Fi task otherwise runs on core 0, which reserves CPU0 interrupt 0 for WMAC, so the "test_esp_intr_dump_expected_output" case no longer finds "Used: RTC_CORE" on interrupt 0 and fails. Pin the Wi-Fi task to core 1 to keep interrupt 0 free.
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1=y

View File

@@ -11,10 +11,22 @@ Two configurations:
at a time via the Unity menu, expecting a panic. at a time via the Unity menu, expecting a panic.
""" """
import os
import pytest import pytest
from pytest_embedded import Dut from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize from pytest_embedded_idf.utils import idf_parametrize
# KASAN instrumentation is not yet emitted under build system v2, so the test
# binary never reports a KASAN error and these cases would always fail. Skip the
# whole module at collection time so no target_test job is even generated for it.
# TODO: IDF-15864
if os.environ.get('IDF_BUILD_V2') == '1':
pytest.skip(
'KASAN is not yet supported under build system v2. TODO: IDF-15864',
allow_module_level=True,
)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# no_halt configuration: all tests pass in one run # no_halt configuration: all tests pass in one run
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@@ -16,3 +16,6 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
# Increase main task stack size # Increase main task stack size
CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096
# CONFIG_VFS_SUPPORT_IO resolves to its default (y) in this test's Kconfig closure; the extra console I/O task makes the core dump header declare more bytes than are emitted, so espcoredump.py info_corefile fails to parse it in the "test_hw_stack_guard_cpu0" case. Disable it.
CONFIG_VFS_SUPPORT_IO=n