mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'task/buildv2_full_pipeline_v6.1' into 'release/v6.1'
Enable full buildv2 pipeline (v6.1) See merge request espressif/esp-idf!50351
This commit is contained in:
@@ -128,6 +128,77 @@ build_child_pipeline:
|
||||
job: generate_build_child_pipeline
|
||||
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:
|
||||
extends:
|
||||
- .build_template
|
||||
|
||||
@@ -353,7 +353,7 @@ build_docker:
|
||||
############################
|
||||
# Host test with test apps #
|
||||
############################
|
||||
test_pytest_qemu:
|
||||
.test_pytest_qemu_template:
|
||||
extends:
|
||||
- .host_test_template
|
||||
artifacts:
|
||||
@@ -363,15 +363,6 @@ test_pytest_qemu:
|
||||
- "**/build*/*.bin"
|
||||
reports:
|
||||
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:
|
||||
- run_cmd idf-ci build run
|
||||
--build-system cmake
|
||||
@@ -386,7 +377,20 @@ test_pytest_qemu:
|
||||
--ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
|
||||
--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:
|
||||
- .host_test_template
|
||||
artifacts:
|
||||
@@ -410,6 +414,10 @@ test_pytest_linux:
|
||||
--junitxml=XUNIT_RESULT.xml
|
||||
--ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
|
||||
|
||||
test_pytest_linux:
|
||||
extends:
|
||||
- .test_pytest_linux_template
|
||||
|
||||
test_pytest_macos:
|
||||
extends:
|
||||
- .host_test_template
|
||||
@@ -516,6 +524,34 @@ pytest_buildv2_system:
|
||||
--junitxml ${CI_PROJECT_DIR}/XUNIT_RESULT.xml
|
||||
--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:
|
||||
extends:
|
||||
- .test_build_system_template
|
||||
|
||||
@@ -19,7 +19,10 @@ ignore_warning_files = [
|
||||
'$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"
|
||||
size_json_filename = "size_${CI_JOB_ID}.json"
|
||||
|
||||
|
||||
@@ -27,10 +27,29 @@ if(EXISTS ${PROJECT_EXTRA_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if(IGNORE_EXTRA_COMPONENT)
|
||||
# Prefix all entries of the list with ${PROJECT_EXTRA_COMPONENTS} absolute path
|
||||
list(TRANSFORM IGNORE_EXTRA_COMPONENT
|
||||
PREPEND "${PROJECT_EXTRA_COMPONENTS}/"
|
||||
OUTPUT_VARIABLE EXTRA_COMPONENT_EXCLUDE_DIRS)
|
||||
foreach(extra_component_dir ${EXTRA_COMPONENT_DIRS})
|
||||
# Strip trailing slash so get_filename_component(NAME) returns the
|
||||
# component name rather than an empty string.
|
||||
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()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
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
|
||||
|
||||
@@ -13,3 +13,6 @@ CONFIG_PARTITION_TABLE_OFFSET=0x9000
|
||||
# HMAC opaque driver test
|
||||
CONFIG_MBEDTLS_TEST_HMAC_OPAQUE_EFUSE_KEY=y
|
||||
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
|
||||
|
||||
@@ -5,3 +5,6 @@ CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=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.
|
||||
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
|
||||
|
||||
@@ -25,44 +25,68 @@ except ImportError:
|
||||
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:
|
||||
"""Get list of expected .gcda file paths for this example."""
|
||||
return [
|
||||
os.path.join(
|
||||
dut.app.binary_path, 'esp-idf', 'main', 'CMakeFiles', '__idf_main.dir', 'gcov_example_main.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'),
|
||||
_resolve_gcda_path(dut.app.binary_path, 'main', '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'),
|
||||
]
|
||||
|
||||
|
||||
def prepare_test(dut: IdfDut) -> list:
|
||||
"""Prepare test environment: create directories and clean up old .gcda files.
|
||||
|
||||
Returns list of expected .gcda file paths.
|
||||
"""
|
||||
# Create the generated .gcda folders
|
||||
# Normally created via `idf.py build`, but in CI non-related files aren't preserved
|
||||
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):
|
||||
"""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
|
||||
# the binary embedded, and clean stale .gcda from either layout.
|
||||
for component in ('main', 'sample'):
|
||||
for d in _candidate_target_dirs(dut.app.binary_path, component):
|
||||
os.makedirs(d, exist_ok=True)
|
||||
try:
|
||||
os.remove(gcda_path)
|
||||
print(f'Removed old .gcda file: {os.path.basename(gcda_path)}')
|
||||
except OSError as e:
|
||||
print(f'Warning: Could not remove {gcda_path}: {e}')
|
||||
for entry in os.listdir(d):
|
||||
if entry.endswith('.gcda'):
|
||||
try:
|
||||
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:
|
||||
expected_gcda_paths = prepare_test(dut)
|
||||
prepare_test(dut)
|
||||
|
||||
def expect_counter_output(loop: int, timeout: int = 10) -> None:
|
||||
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
|
||||
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)
|
||||
if coverage: # Only verify if detailed data is available
|
||||
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
|
||||
dut.serial.close()
|
||||
|
||||
expected_gcda_paths = prepare_test(dut)
|
||||
prepare_test(dut)
|
||||
log_file = os.path.join(dut.logdir, 'gcov_uart.log')
|
||||
uart_port = dut.serial.port
|
||||
baud = dut.app.sdkconfig.get('APPTRACE_UART_BAUDRATE')
|
||||
@@ -178,6 +204,8 @@ def _test_gcov_uart(dut: IdfDut) -> None:
|
||||
# Verify each dump
|
||||
for dump_num, expected in enumerate(expected_counts, start=1):
|
||||
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)
|
||||
|
||||
if coverage: # Only verify details if coverage data is available
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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'] = {
|
||||
'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()
|
||||
@@ -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()
|
||||
@@ -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/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_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' 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
|
||||
@@ -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: \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 .*/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*
|
||||
|
||||
@@ -38,8 +38,9 @@ function(__generate_gdbinit)
|
||||
set(symbols_gdbinit_path ${gdbinit_dir}/symbols)
|
||||
set(py_extensions_gdbinit_path ${gdbinit_dir}/py_extensions)
|
||||
set(connect_gdbinit_path ${gdbinit_dir}/connect)
|
||||
idf_build_get_property(PROJECT_EXECUTABLE EXECUTABLE)
|
||||
set(application_elf ${BUILD_DIR}/${PROJECT_EXECUTABLE})
|
||||
# EXECUTABLE is a CMake target name; the on-disk elf is EXECUTABLE_NAME + ".elf".
|
||||
idf_build_get_property(PROJECT_EXECUTABLE_NAME EXECUTABLE_NAME)
|
||||
set(application_elf ${BUILD_DIR}/${PROJECT_EXECUTABLE_NAME}.elf)
|
||||
|
||||
file(MAKE_DIRECTORY ${gdbinit_dir})
|
||||
|
||||
|
||||
@@ -36,35 +36,21 @@ set(MENUCONFIG_INLINE_MIN_KCONFIG_VERSION "3.9.0")
|
||||
function(__init_kconfig)
|
||||
idf_build_get_property(idf_path IDF_PATH)
|
||||
|
||||
# Initialize SDKCONFIG and SDKCONFIG_DEFAULTS build properties using environment
|
||||
# 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()
|
||||
|
||||
# Initialize the SDKCONFIG file path build property.
|
||||
__get_default_value(VARIABLE SDKCONFIG
|
||||
DEFAULT "${CMAKE_SOURCE_DIR}/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_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_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)
|
||||
|
||||
# Setup ESP-IDF root Kconfig and sdkconfig.rename files.
|
||||
@@ -78,6 +64,54 @@ function(__init_kconfig)
|
||||
idf_build_set_property(CONFIG_DIR "${config_dir}")
|
||||
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()
|
||||
|
||||
@@ -187,6 +221,13 @@ endfunction()
|
||||
4. Generate all output files (sdkconfig.h, sdkconfig.cmake, etc.)
|
||||
#]]
|
||||
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
|
||||
__consolidate_component_kconfig_files()
|
||||
|
||||
|
||||
2
tools/test_apps/system/esp_intr_dump/sdkconfig.defaults
Normal file
2
tools/test_apps/system/esp_intr_dump/sdkconfig.defaults
Normal 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
|
||||
@@ -16,3 +16,6 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
|
||||
# Increase main task stack size
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user