mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'task/buildv2_gdbinit_multi_exec' into 'master'
feat(cmakev2): parameterize gdbinit for multi-executable builds Closes IDF-15860 See merge request espressif/esp-idf!51239
This commit is contained in:
@@ -30,6 +30,17 @@ Each executable then gets its own binary, flash, and configuration targets, name
|
||||
|
||||
A single build now produces both ``app1.bin`` and ``app2.bin``, each with its own ``appN-flash`` and ``appN-menuconfig`` targets.
|
||||
|
||||
To emit the ``project_description.json`` and gdbinit files that tooling such as ``idf.py gdb`` consumes, call :cmakev2:ref:`idf_build_generate_metadata` for each executable. These outputs are written to project-wide default paths, so every executable past the first must be given a distinct ``OUTPUT_FILE`` and ``GDBINIT_DIR`` to keep its metadata and gdbinit files from overwriting the others':
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_build_generate_metadata(BINARY app1_binary)
|
||||
idf_build_generate_metadata(BINARY app2_binary
|
||||
OUTPUT_FILE "${CMAKE_BINARY_DIR}/project_description_app2.json"
|
||||
GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app2")
|
||||
|
||||
Each executable's ``project_description.json`` then references its own ELF and points ``gdbinit_files`` at its own directory, so a debugger loads the symbols of the executable it was launched for.
|
||||
|
||||
.. note::
|
||||
|
||||
A multi-binary project has a single project-wide ``sdkconfig``, and a
|
||||
|
||||
@@ -32,6 +32,8 @@ idf_flash_binary(app1_binary
|
||||
# Generate flasher_args.json and metadata for the primary app1 binary.
|
||||
# app1 is the only app registered in the global 'flash' target (app2 omits FLASH),
|
||||
# so flasher_args.json correctly contains app1 as the sole app binary.
|
||||
# app1 uses the default output paths (build/project_description.json and
|
||||
# build/gdbinit). app2 below must use distinct paths so it does not overwrite them.
|
||||
idf_build_generate_flasher_args()
|
||||
idf_build_generate_metadata(BINARY app1_binary)
|
||||
|
||||
@@ -52,6 +54,14 @@ idf_flash_binary(app2_binary
|
||||
TARGET app2-flash
|
||||
NAME "app2")
|
||||
|
||||
# Generate metadata for app2. idf_build_generate_metadata writes the
|
||||
# project_description.json and the gdbinit files to shared default paths, so
|
||||
# pass a distinct OUTPUT_FILE and GDBINIT_DIR to keep app2's files from
|
||||
# overwriting app1's.
|
||||
idf_build_generate_metadata(BINARY app2_binary
|
||||
OUTPUT_FILE "${CMAKE_BINARY_DIR}/project_description_app2.json"
|
||||
GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app2")
|
||||
|
||||
# Create menuconfig and confserver targets for app2 binary
|
||||
idf_create_menuconfig(app2.elf TARGET app2-menuconfig)
|
||||
idf_create_confserver(app2.elf TARGET app2-confserver)
|
||||
|
||||
@@ -932,7 +932,8 @@ endfunction()
|
||||
idf_build_generate_metadata([BINARY <binary>]
|
||||
[EXECUTABLE <executable>]
|
||||
[OUTPUT_FILE <file>]
|
||||
[HINTS_OUTPUT_FILE <file>])
|
||||
[HINTS_OUTPUT_FILE <file>]
|
||||
[GDBINIT_DIR <dir>])
|
||||
|
||||
*BINARY[in,opt]*
|
||||
|
||||
@@ -954,6 +955,15 @@ endfunction()
|
||||
behaviour prevents hint files from different binaries overwriting each
|
||||
other in multi-binary projects.
|
||||
|
||||
*GDBINIT_DIR[in,opt]*
|
||||
|
||||
Optional directory for the generated gdbinit files. If not provided,
|
||||
the default location ``<build>/gdbinit`` is used. Multi-executable
|
||||
projects should pass a distinct directory per executable so their
|
||||
gdbinit files do not overwrite each other. Only the per-executable
|
||||
``symbols``, ``connect`` and ``py_extensions`` files are written here;
|
||||
the project-wide ``prefix_map`` file stays under ``<build>/gdbinit``.
|
||||
|
||||
Generate metadata for the specified ``binary`` or ``executable`` target and
|
||||
store it in the specified ``OUTPUT_FILE``. If no ``OUTPUT_FILE`` is
|
||||
provided, the default location ``<build>/project_description.json`` will be
|
||||
@@ -961,7 +971,7 @@ endfunction()
|
||||
#]]
|
||||
function(idf_build_generate_metadata)
|
||||
set(options)
|
||||
set(one_value OUTPUT_FILE BINARY EXECUTABLE HINTS_OUTPUT_FILE)
|
||||
set(one_value OUTPUT_FILE BINARY EXECUTABLE HINTS_OUTPUT_FILE GDBINIT_DIR)
|
||||
set(multi_value)
|
||||
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
||||
|
||||
@@ -1037,11 +1047,17 @@ function(idf_build_generate_metadata)
|
||||
idf_build_get_property(component_interfaces COMPONENT_INTERFACES)
|
||||
__get_components_metadata(COMPONENTS "${component_interfaces}" OUTPUT all_component_info_json)
|
||||
|
||||
__generate_gdbinit()
|
||||
if(NOT DEFINED ARG_GDBINIT_DIR)
|
||||
set(ARG_GDBINIT_DIR "${BUILD_DIR}/gdbinit")
|
||||
endif()
|
||||
|
||||
# Each executable produces its own gdbinit output referencing its own ELF.
|
||||
# __generate_gdbinit resolves the ELF path from the executable target.
|
||||
__generate_gdbinit("${ARG_EXECUTABLE}" "${ARG_GDBINIT_DIR}"
|
||||
gdbinit_files_symbols gdbinit_files_py_extensions gdbinit_files_connect)
|
||||
# prefix_map is project-wide (set once in project.cmake), unlike the
|
||||
# per-executable files returned by __generate_gdbinit above.
|
||||
idf_build_get_property(gdbinit_files_prefix_map GDBINIT_FILES_PREFIX_MAP)
|
||||
idf_build_get_property(gdbinit_files_symbols GDBINIT_FILES_SYMBOLS)
|
||||
idf_build_get_property(gdbinit_files_py_extensions GDBINIT_FILES_PY_EXTENSIONS)
|
||||
idf_build_get_property(gdbinit_files_connect GDBINIT_FILES_CONNECT)
|
||||
__get_openocd_options(debug_arguments_openocd)
|
||||
|
||||
if(NOT DEFINED ARG_OUTPUT_FILE)
|
||||
|
||||
136
tools/cmakev2/gdbinit.cmake
Normal file
136
tools/cmakev2/gdbinit.cmake
Normal file
@@ -0,0 +1,136 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[[
|
||||
__generate_gdbinit(<executable> <gdbinit_dir>
|
||||
<out_symbols> <out_py_extensions> <out_connect>)
|
||||
|
||||
*executable[in]*
|
||||
|
||||
Executable target whose ELF the debugger loads the application symbols
|
||||
from. The ELF path is resolved from the target.
|
||||
|
||||
*gdbinit_dir[in]*
|
||||
|
||||
Directory into which the generated gdbinit files are written.
|
||||
|
||||
*out_symbols[out]*, *out_py_extensions[out]*, *out_connect[out]*
|
||||
|
||||
Names of variables set in the caller's scope to the paths of the
|
||||
generated ``symbols``, ``py_extensions`` and ``connect`` files. These
|
||||
paths are per-executable, so they are returned to the caller instead of
|
||||
stored in global build properties, which in a multi-executable build
|
||||
would only ever hold the last executable's paths.
|
||||
|
||||
Prepare the gdbinit files (``symbols``, ``connect``, ``py_extensions`` and
|
||||
the combined ``gdbinit``) passed to the debugger. Taking the executable
|
||||
target and output directory as arguments lets a project that builds several
|
||||
executables produce a distinct set of gdbinit files per executable instead
|
||||
of overwriting a single shared directory.
|
||||
#]]
|
||||
function(__generate_gdbinit executable gdbinit_dir out_symbols out_py_extensions out_connect)
|
||||
if(NOT TARGET "${executable}")
|
||||
idf_die("The executable '${executable}' is not a cmake target")
|
||||
endif()
|
||||
|
||||
# The output paths derive from gdbinit_dir alone, so return them on every
|
||||
# call, including the repeat calls that skip regeneration below.
|
||||
set(symbols_gdbinit_path "${gdbinit_dir}/symbols")
|
||||
set(py_extensions_gdbinit_path "${gdbinit_dir}/py_extensions")
|
||||
set(connect_gdbinit_path "${gdbinit_dir}/connect")
|
||||
set(${out_symbols} "${symbols_gdbinit_path}" PARENT_SCOPE)
|
||||
set(${out_py_extensions} "${py_extensions_gdbinit_path}" PARENT_SCOPE)
|
||||
set(${out_connect} "${connect_gdbinit_path}" PARENT_SCOPE)
|
||||
|
||||
# CMake permits only one file(GENERATE) per output path, and the same
|
||||
# executable can be passed to idf_build_generate_metadata more than once
|
||||
# (e.g. as EXECUTABLE and again as its BINARY). Generate the gdbinit files
|
||||
# only the first time a directory is used, keyed by the hash of its absolute
|
||||
# path so that differently-spelled paths resolving to the same directory are
|
||||
# matched. A different executable reusing the same directory is a collision.
|
||||
get_filename_component(gdbinit_dir_abs "${gdbinit_dir}" ABSOLUTE
|
||||
BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
string(MD5 gdbinit_dir_key "${gdbinit_dir_abs}")
|
||||
idf_build_get_property(gdbinit_dir_owner __GDBINIT_OWNER_${gdbinit_dir_key})
|
||||
if(gdbinit_dir_owner)
|
||||
if(NOT gdbinit_dir_owner STREQUAL executable)
|
||||
idf_die("Executables '${gdbinit_dir_owner}' and '${executable}' both generate gdbinit "
|
||||
"files into '${gdbinit_dir}'. Pass a distinct GDBINIT_DIR per executable.")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
idf_build_set_property(__GDBINIT_OWNER_${gdbinit_dir_key} "${executable}")
|
||||
|
||||
idf_build_get_property(idf_path IDF_PATH)
|
||||
idf_build_get_property(python PYTHON)
|
||||
file(MAKE_DIRECTORY "${gdbinit_dir}")
|
||||
|
||||
# Resolve the ELF path from the target instead of reconstructing it from
|
||||
# OUTPUT_NAME and SUFFIX.
|
||||
set(application_elf "$<TARGET_FILE:${executable}>")
|
||||
|
||||
# Define static gdbinit commands
|
||||
if(CONFIG_IDF_TARGET_LINUX)
|
||||
set(gdbinit_connect
|
||||
"# Run the application and stop on app_main()\n"
|
||||
"break app_main\n"
|
||||
"run\n")
|
||||
else()
|
||||
set(gdbinit_connect
|
||||
"# Connect to the default openocd-esp port and stop on app_main()\n"
|
||||
"set remotetimeout 10\n"
|
||||
"target remote :3333\n"
|
||||
"monitor reset halt\n"
|
||||
"maintenance flush register-cache\n"
|
||||
"thbreak app_main\n"
|
||||
"continue\n")
|
||||
endif()
|
||||
|
||||
set(gdbinit_py_extensions
|
||||
"# Add Python GDB extensions\n"
|
||||
"python\n"
|
||||
"import sys\n"
|
||||
"try:\n"
|
||||
" import freertos_gdb\n"
|
||||
"except ModuleNotFoundError:\n"
|
||||
" print('warning: python extension \"freertos_gdb\" not found.', file=sys.stderr)\n"
|
||||
"try:\n"
|
||||
" import idf_drivers_gdb\n"
|
||||
"except ModuleNotFoundError:\n"
|
||||
" print('warning: python extension \"idf_drivers_gdb\" not found.', file=sys.stderr)\n"
|
||||
"end\n")
|
||||
|
||||
# Get ROM ELFs gdbinit part
|
||||
if(CONFIG_IDF_TARGET_LINUX)
|
||||
set(rom_symbols)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND ${python} "${idf_path}/components/esp_rom/gen_gdbinit.py" ${IDF_TARGET}
|
||||
OUTPUT_VARIABLE rom_symbols
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
if(NOT result EQUAL 0)
|
||||
set(rom_symbols)
|
||||
message(WARNING "Error while generating esp_rom gdbinit")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check if bootloader ELF is defined and set symbol-file accordingly
|
||||
if(DEFINED BOOTLOADER_ELF_FILE)
|
||||
set(add_bootloader_symbols " add-symbol-file ${BOOTLOADER_ELF_FILE}")
|
||||
else()
|
||||
set(add_bootloader_symbols " # Bootloader elf was not found")
|
||||
endif()
|
||||
|
||||
# application_elf is a generator expression, which configure_file() does not
|
||||
# expand, so the configured template is routed through file_generate.
|
||||
configure_file("${idf_path}/tools/cmake/symbols.gdbinit.in" "${symbols_gdbinit_path}.templ")
|
||||
file(READ "${symbols_gdbinit_path}.templ" symbols_gdbinit_templ)
|
||||
file(REMOVE "${symbols_gdbinit_path}.templ")
|
||||
file_generate("${symbols_gdbinit_path}" CONTENT "${symbols_gdbinit_templ}")
|
||||
file(WRITE "${py_extensions_gdbinit_path}" ${gdbinit_py_extensions})
|
||||
file(WRITE "${connect_gdbinit_path}" ${gdbinit_connect})
|
||||
|
||||
file(WRITE "${gdbinit_dir}/gdbinit" "source ${symbols_gdbinit_path}\n")
|
||||
file(APPEND "${gdbinit_dir}/gdbinit" "source ${connect_gdbinit_path}\n")
|
||||
endfunction()
|
||||
@@ -21,10 +21,11 @@ set(CMAKE_MODULE_PATH
|
||||
# for both cmakev1 and cmakev2.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/version.cmake)
|
||||
|
||||
# The gdbinit.cmake file from cmakev1 contains a single function,
|
||||
# __generate_gdbinit, which is used in the generation of
|
||||
# project_description.json.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/gdbinit.cmake)
|
||||
# gdbinit.cmake provides __generate_gdbinit, used to produce the per-executable
|
||||
# gdbinit files referenced from project_description.json. It takes the
|
||||
# application ELF path and output directory as arguments so multi-executable
|
||||
# projects do not overwrite a single shared gdbinit directory.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/gdbinit.cmake)
|
||||
|
||||
# The openocd.cmake file from cmakev1 contains a single function,
|
||||
# __get_openocd_options, which is used in the generation of
|
||||
|
||||
152
tools/test_build_system/buildv2/test_gdbinit.py
Normal file
152
tools/test_build_system/buildv2/test_gdbinit.py
Normal file
@@ -0,0 +1,152 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from test_build_system_helpers import IdfPyFunc
|
||||
from test_build_system_helpers import replace_in_file
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('test_app_copy')
|
||||
def test_gdbinit_single_binary(idf_py: IdfPyFunc) -> None:
|
||||
"""A single-executable build writes gdbinit into build/gdbinit/ referencing the app elf."""
|
||||
logging.info('Testing gdbinit: single-binary default output directory and elf reference')
|
||||
|
||||
idf_py('build')
|
||||
|
||||
symbols = Path('build/gdbinit/symbols')
|
||||
assert symbols.exists(), 'build/gdbinit/symbols was not generated for a single-binary build'
|
||||
content = symbols.read_text()
|
||||
assert 'build_test_app.elf' in content, f'gdbinit symbols must reference the application elf, got: {content!r}'
|
||||
|
||||
|
||||
def _create_app2_component(base_dir: Path) -> None:
|
||||
"""Create a second application component for multi-binary tests."""
|
||||
app2_dir = base_dir / 'components' / 'app2'
|
||||
app2_dir.mkdir(parents=True, exist_ok=True)
|
||||
(app2_dir / 'CMakeLists.txt').write_text('idf_component_register(SRCS "app2.c")\n')
|
||||
(app2_dir / 'app2.c').write_text('void app_main(void) {}\n')
|
||||
|
||||
|
||||
def _write_two_executable_cmakelists(app1_extra: str = '', app2_extra: str = '') -> None:
|
||||
"""Rewrite the app CMakeLists to build app1 and app2 and generate metadata for each.
|
||||
|
||||
app1_extra and app2_extra are appended inside the respective
|
||||
idf_build_generate_metadata() calls (e.g. a ``GDBINIT_DIR`` argument).
|
||||
"""
|
||||
_create_app2_component(Path('.'))
|
||||
replace_in_file(
|
||||
'CMakeLists.txt',
|
||||
'idf_project_default()',
|
||||
'idf_project_init()\n'
|
||||
'idf_build_executable(app1 COMPONENTS main)\n'
|
||||
'idf_build_executable(app2 COMPONENTS app2)\n'
|
||||
'add_custom_target(app ALL DEPENDS app1 app2)\n'
|
||||
f'idf_build_generate_metadata(EXECUTABLE app1{app1_extra})\n'
|
||||
'idf_build_generate_metadata(EXECUTABLE app2\n'
|
||||
' OUTPUT_FILE "${CMAKE_BINARY_DIR}/project_description_app2.json"'
|
||||
f'{app2_extra})\n',
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('test_app_copy')
|
||||
def test_gdbinit_multi_binary_per_executable(idf_py: IdfPyFunc) -> None:
|
||||
"""Each executable in a multi-binary build gets its own gdbinit dir referencing its own elf."""
|
||||
logging.info('Testing gdbinit: per-executable output directories in a multi-binary build')
|
||||
|
||||
_write_two_executable_cmakelists(
|
||||
'\n GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app1"',
|
||||
'\n GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app2"',
|
||||
)
|
||||
|
||||
idf_py('build')
|
||||
|
||||
app1_symbols = Path('build/gdbinit/app1/symbols')
|
||||
app2_symbols = Path('build/gdbinit/app2/symbols')
|
||||
|
||||
assert app1_symbols.exists(), 'per-executable gdbinit for app1 was not generated'
|
||||
assert app2_symbols.exists(), 'per-executable gdbinit for app2 was not generated'
|
||||
|
||||
app1_content = app1_symbols.read_text()
|
||||
app2_content = app2_symbols.read_text()
|
||||
|
||||
# Each executable's gdbinit must reference its own elf, proving the outputs
|
||||
# did not overwrite each other in the shared build/gdbinit/ directory.
|
||||
assert 'app1.elf' in app1_content, f'app1 gdbinit must reference app1.elf, got: {app1_content!r}'
|
||||
assert 'app2.elf' in app2_content, f'app2 gdbinit must reference app2.elf, got: {app2_content!r}'
|
||||
assert 'app2.elf' not in app1_content, 'app1 gdbinit was overwritten with app2 symbols'
|
||||
assert 'app1.elf' not in app2_content, 'app2 gdbinit was overwritten with app1 symbols'
|
||||
|
||||
# Each executable's project_description.json must point gdbinit_files at that
|
||||
# executable's GDBINIT_DIR, which is what idf.py gdb/debug consumes.
|
||||
app1_desc = json.loads(Path('build/project_description.json').read_text())
|
||||
app2_desc = json.loads(Path('build/project_description_app2.json').read_text())
|
||||
app1_symbols_path = app1_desc['gdbinit_files']['01_symbols']
|
||||
app2_symbols_path = app2_desc['gdbinit_files']['01_symbols']
|
||||
assert app1_symbols_path.endswith('gdbinit/app1/symbols'), app1_symbols_path
|
||||
assert app2_symbols_path.endswith('gdbinit/app2/symbols'), app2_symbols_path
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('test_app_copy')
|
||||
@pytest.mark.test_app_copy('examples/build_system/cmakev2/features/idf_as_lib', 'idf_as_lib')
|
||||
def test_gdbinit_add_executable_elf_target(idf_py: IdfPyFunc) -> None:
|
||||
"""A plain add_executable(<name>.elf) target references the ELF that exists on disk."""
|
||||
logging.info('Testing gdbinit: add_executable(.elf) target ELF resolution (idf_as_lib pattern)')
|
||||
|
||||
idf_py('build')
|
||||
|
||||
symbols = Path('build/gdbinit/symbols')
|
||||
assert symbols.exists(), 'build/gdbinit/symbols was not generated'
|
||||
match = re.search(r'^file (\S+)$', symbols.read_text(), re.MULTILINE)
|
||||
assert match, f'no application "file" line in gdbinit symbols: {symbols.read_text()!r}'
|
||||
elf_ref = match.group(1)
|
||||
|
||||
# The executable target is named idf_as_lib.elf (add_executable, no OUTPUT_NAME
|
||||
# or SUFFIX): the ELF on disk is idf_as_lib.elf, not idf_as_lib.elf.elf, so the
|
||||
# gdbinit must reference a file that actually exists.
|
||||
assert Path(elf_ref).name == 'idf_as_lib.elf', f'gdbinit must reference the real ELF, got: {elf_ref!r}'
|
||||
assert Path(elf_ref).exists(), f'gdbinit references a non-existent ELF: {elf_ref!r}'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('test_app_copy')
|
||||
@pytest.mark.parametrize(
|
||||
'app1_extra, app2_extra',
|
||||
[
|
||||
pytest.param(
|
||||
'\n GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/shared"',
|
||||
'\n GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/shared"',
|
||||
id='explicit-shared-dir',
|
||||
),
|
||||
pytest.param('', '', id='both-default-dir'),
|
||||
],
|
||||
)
|
||||
def test_gdbinit_shared_dir_rejected(idf_py: IdfPyFunc, app1_extra: str, app2_extra: str) -> None:
|
||||
"""Two different executables generating gdbinit into the same directory is rejected at configure time."""
|
||||
logging.info('Testing gdbinit: two executables sharing a GDBINIT_DIR is rejected')
|
||||
|
||||
_write_two_executable_cmakelists(app1_extra, app2_extra)
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError) as exc_info:
|
||||
idf_py('reconfigure')
|
||||
err_output = (exc_info.value.stdout or '') + (exc_info.value.stderr or '')
|
||||
assert 'both generate gdbinit' in err_output, f'expected a GDBINIT_DIR collision error, got: {err_output!r}'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('test_app_copy')
|
||||
def test_gdbinit_similar_dir_names_not_conflated(idf_py: IdfPyFunc) -> None:
|
||||
"""GDBINIT_DIRs that differ only by punctuation are distinct directories, not a false collision."""
|
||||
logging.info('Testing gdbinit: dirs differing only by punctuation are not conflated')
|
||||
|
||||
_write_two_executable_cmakelists(
|
||||
'\n GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app-x"',
|
||||
'\n GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app_x"',
|
||||
)
|
||||
|
||||
idf_py('reconfigure')
|
||||
|
||||
assert Path('build/gdbinit/app-x/symbols').exists(), 'gdbinit for app-x was not generated'
|
||||
assert Path('build/gdbinit/app_x/symbols').exists(), 'gdbinit for app_x was not generated'
|
||||
@@ -48,11 +48,14 @@ def test_multi_binary_all_features(idf_py: IdfPyFunc) -> None:
|
||||
# Build both binaries
|
||||
'add_custom_target(app ALL DEPENDS app1_bin app2_bin)\n'
|
||||
# Generate metadata for both executables and binaries
|
||||
'idf_build_generate_metadata(EXECUTABLE app1)\n'
|
||||
'idf_build_generate_metadata(EXECUTABLE app1\n'
|
||||
' GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app1")\n'
|
||||
'idf_build_generate_metadata(EXECUTABLE app2\n'
|
||||
' OUTPUT_FILE "${CMAKE_BINARY_DIR}/project_description_app2.json")\n'
|
||||
' OUTPUT_FILE "${CMAKE_BINARY_DIR}/project_description_app2.json"\n'
|
||||
' GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app2")\n'
|
||||
'idf_build_generate_metadata(BINARY app1_bin\n'
|
||||
' OUTPUT_FILE "${CMAKE_BINARY_DIR}/project_description_bin.json")\n'
|
||||
' OUTPUT_FILE "${CMAKE_BINARY_DIR}/project_description_bin.json"\n'
|
||||
' GDBINIT_DIR "${CMAKE_BINARY_DIR}/gdbinit/app1_bin")\n'
|
||||
# Create menuconfig and confserver targets
|
||||
'idf_create_menuconfig(app1 TARGET menuconfig-app1)\n'
|
||||
'idf_create_menuconfig(app2 TARGET menuconfig-app2)\n'
|
||||
|
||||
Reference in New Issue
Block a user