mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-02 18:10:57 +02:00
Merge branch 'fix/build_system_create_bin_gen_tgts' into 'master'
feat(build-system): Create build system hooks for post-elf processing Closes IDFGH-16204 See merge request espressif/esp-idf!41097
This commit is contained in:
@@ -526,6 +526,10 @@ function(__idf_build_binary OUTPUT_BIN_FILENAME TARGET_NAME)
|
||||
idf_build_get_property(elf EXECUTABLE GENERATOR_EXPRESSION)
|
||||
idf_component_get_property(esptool_py_cmd esptool_py ESPTOOLPY_CMD)
|
||||
|
||||
# Collect post-ELF dependencies for the current ELF file
|
||||
idf_build_get_property(elf_name EXECUTABLE_NAME)
|
||||
idf_build_get_post_elf_dependencies("${elf_name}.elf" post_elf_deps)
|
||||
|
||||
# Get esptool.py arguments for elf2image target
|
||||
idf_component_get_property(esptool_elf2image_args esptool_py ESPTOOL_PY_ELF2IMAGE_ARGS)
|
||||
|
||||
@@ -535,7 +539,7 @@ function(__idf_build_binary OUTPUT_BIN_FILENAME TARGET_NAME)
|
||||
-o "${build_dir}/${OUTPUT_BIN_FILENAME}" "$<TARGET_FILE:$<GENEX_EVAL:${elf}>>"
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Generated ${build_dir}/${OUTPUT_BIN_FILENAME}"
|
||||
COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${OUTPUT_BIN_FILENAME}" > "${build_dir}/.bin_timestamp"
|
||||
DEPENDS "$<TARGET_FILE:$<GENEX_EVAL:${elf}>>"
|
||||
DEPENDS "$<TARGET_FILE:$<GENEX_EVAL:${elf}>>" ${post_elf_deps}
|
||||
VERBATIM
|
||||
WORKING_DIRECTORY ${build_dir}
|
||||
COMMENT "Generating binary image from built executable"
|
||||
|
@@ -1342,6 +1342,50 @@ Specify the executable *executable* for ESP-IDF build. This attaches additional
|
||||
|
||||
Get the value of the specified config. Much like build properties, specifying *GENERATOR_EXPRESSION* will retrieve the generator expression string for that config, instead of the actual value, which can be used with CMake commands that support generator expressions. Actual config values are only known after call to ``idf_build_process``, however.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
idf_build_add_post_elf_dependency(elf_filename dep_target)
|
||||
|
||||
Register a dependency that must run after the ELF is linked (post-ELF) and before the binary image is generated. This is useful when a component needs to post‑process the ELF in place prior to ``elf2image`` execution (for example, inserting metadata, stripping sections, or generating additional symbol files). The dependency target ``dep_target`` must be a valid CMake target. If your rule reads or modifies the ELF, declare the ELF file as a ``DEPENDS`` of your custom command.
|
||||
|
||||
.. important::
|
||||
|
||||
When creating post‑ELF steps, ensure the build graph remains acyclic:
|
||||
|
||||
- Do not make the ELF itself the output of your custom command. Produce a separate output (for example, ``app.elf.post``, ``app.elf.symbols``, or a simple marker file).
|
||||
- If you must modify the ELF in place, also produce an additional output file and update its timestamp to be newer than the ELF after modification (for example, using ``cmake -E touch``). This ensures the output file has a newer timestamp than the modified ELF, so CMake considers the rule satisfied and won't re-run it on subsequent builds.
|
||||
|
||||
Following these rules ensures the post‑ELF hook runs in the intended order without triggering infinite rebuild loops.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
# Create a custom command to process the ELF file after linking
|
||||
idf_build_get_property(elf_target EXECUTABLE GENERATOR_EXPRESSION)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.stripped_marker"
|
||||
COMMAND ${CMAKE_OBJCOPY} --strip-debug
|
||||
"$<TARGET_FILE:$<GENEX_EVAL:${elf_target}>>"
|
||||
COMMAND ${CMAKE_COMMAND} -E touch
|
||||
"${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.stripped_marker"
|
||||
DEPENDS "$<TARGET_FILE:$<GENEX_EVAL:${elf_target}>>"
|
||||
)
|
||||
|
||||
# Wrap it in a custom target
|
||||
add_custom_target(strip_elf DEPENDS
|
||||
"${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.stripped_marker"
|
||||
)
|
||||
|
||||
# Register it to run after the ELF is linked but before the BIN is generated
|
||||
idf_build_add_post_elf_dependency("${CMAKE_PROJECT_NAME}.elf" strip_elf)
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
idf_build_get_post_elf_dependencies(elf_filename out_var)
|
||||
|
||||
Retrieve the list of post-ELF dependencies registered for the given ELF file and store it in ``out_var``.
|
||||
|
||||
|
||||
.. _cmake-build-properties:
|
||||
|
||||
|
@@ -1343,6 +1343,51 @@ ESP-IDF 构建命令
|
||||
获取指定配置的值。就像构建属性一样,特定 *GENERATOR_EXPRESSION* 将检索该配置的生成器表达式字符串,而不是实际值,即可以与支持生成器表达式的 CMake 命令一起使用。然而,实际的配置值只有在调用 ``idf_build_process`` 后才能知道。
|
||||
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
idf_build_add_post_elf_dependency(elf_filename dep_target)
|
||||
|
||||
注册一个在 ELF 链接完成之后(post-ELF)且在生成二进制镜像之前必须运行的依赖。适用于组件在执行 ``elf2image`` 之前需要对 ELF 进行就地处理的场景(例如,插入元数据、剥离段或生成额外的符号文件)。依赖目标 ``dep_target`` 必须是一个有效的 CMake 目标。如果您的规则读取或修改 ELF,请将 ELF 文件声明为自定义命令的 ``DEPENDS``。
|
||||
|
||||
.. important:: 避免构建循环
|
||||
|
||||
创建 post-ELF 步骤时,确保构建图保持无环:
|
||||
|
||||
- 不要将 ELF 本身作为自定义命令的输出。产生一个单独的输出(例如,``app.elf.post``、``app.elf.symbols`` 或简单的标记文件)。
|
||||
- 如果必须就地修改 ELF,还要产生一个额外的输出文件,并将其时间戳更新为比修改后的 ELF 更新(例如,使用 ``cmake -E touch``)。这确保输出文件比修改后的 ELF 具有更新的时间戳,因此 CMake 认为规则已满足,不会在后续构建中重新运行它。
|
||||
- 遵循这些规则可确保 post-ELF 钩子按预期顺序运行,而不会触发无限重建循环。
|
||||
|
||||
示例:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
# 创建一个自定义命令来在链接后处理 ELF 文件
|
||||
idf_build_get_property(elf_target EXECUTABLE GENERATOR_EXPRESSION)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.stripped_marker"
|
||||
COMMAND ${CMAKE_OBJCOPY} --strip-debug
|
||||
"$<TARGET_FILE:$<GENEX_EVAL:${elf_target}>>"
|
||||
COMMAND ${CMAKE_COMMAND} -E touch
|
||||
"${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.stripped_marker"
|
||||
DEPENDS "$<TARGET_FILE:$<GENEX_EVAL:${elf_target}>>"
|
||||
)
|
||||
|
||||
# 将其包装在自定义目标中
|
||||
add_custom_target(strip_elf DEPENDS
|
||||
"${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.stripped_marker"
|
||||
)
|
||||
|
||||
# 注册它在 ELF 链接后但在 BIN 生成前运行
|
||||
idf_build_add_post_elf_dependency("${CMAKE_PROJECT_NAME}.elf" strip_elf)
|
||||
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
idf_build_get_post_elf_dependencies(elf_filename out_var)
|
||||
|
||||
获取指定 ELF 文件名 ``elf_filename`` 已注册的 post-ELF 依赖列表,并将结果存入 ``out_var``。
|
||||
|
||||
|
||||
.. _cmake-build-properties:
|
||||
|
||||
ESP-IDF 构建属性
|
||||
|
@@ -94,6 +94,49 @@ function(idf_build_replace_option_from_property property_name option_to_remove n
|
||||
idf_build_set_property(${property_name} "${new_list_of_options}")
|
||||
endfunction()
|
||||
|
||||
# idf_build_add_post_elf_dependency
|
||||
#
|
||||
# @brief Register a dependency that must run after the ELF is linked (post-ELF) and before
|
||||
# the binary image is generated.
|
||||
#
|
||||
# @param[in] elf_filename The filename of the ELF file that the dependency must run after.
|
||||
# @param[in] dep_target The target that must run after the ELF is linked.
|
||||
#
|
||||
# @note This function is used by components to register a post-ELF hook.
|
||||
#
|
||||
# Example usage:
|
||||
# idf_build_add_post_elf_dependency("${CMAKE_PROJECT_NAME}.elf" <dep_target>)
|
||||
#
|
||||
function(idf_build_add_post_elf_dependency elf_filename dep_target)
|
||||
if("${elf_filename}" STREQUAL "")
|
||||
message(FATAL_ERROR "elf filename must be provided (e.g. ${CMAKE_PROJECT_NAME}.elf)")
|
||||
endif()
|
||||
if(NOT TARGET ${dep_target})
|
||||
message(FATAL_ERROR "dependency '${dep_target}' is not a known CMake target")
|
||||
endif()
|
||||
|
||||
# Append dependency to this ELF's dep list
|
||||
idf_build_get_property(deps "__POST_ELF_DEPS_${elf_filename}")
|
||||
list(APPEND deps "${dep_target}")
|
||||
list(REMOVE_DUPLICATES deps)
|
||||
idf_build_set_property("__POST_ELF_DEPS_${elf_filename}" "${deps}")
|
||||
endfunction()
|
||||
|
||||
# idf_build_get_post_elf_dependencies
|
||||
#
|
||||
# @brief Retrieve the dependencies for the given ELF filename.
|
||||
#
|
||||
# @param[in] elf_filename The filename of the ELF file to get the dependencies for.
|
||||
# @param[out] out_var The variable to store the dependencies in.
|
||||
#
|
||||
# Example usage:
|
||||
# idf_build_get_post_elf_dependencies("${CMAKE_PROJECT_NAME}.elf" post_elf_deps)
|
||||
#
|
||||
function(idf_build_get_post_elf_dependencies elf_filename out_var)
|
||||
idf_build_get_property(deps "__POST_ELF_DEPS_${elf_filename}")
|
||||
set(${out_var} "${deps}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
#
|
||||
# Retrieve the IDF_PATH repository's version, either using a version
|
||||
# file or Git revision. Sets the IDF_VER build property.
|
||||
|
44
tools/test_build_system/test_post_elf_dependency.py
Normal file
44
tools/test_build_system/test_post_elf_dependency.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
from pathlib import Path
|
||||
|
||||
from test_build_system_helpers import append_to_file
|
||||
from test_build_system_helpers import get_snapshot
|
||||
from test_build_system_helpers import run_cmake_and_build
|
||||
|
||||
|
||||
def test_post_elf_dependency_runs_before_bin(test_app_copy: Path) -> None:
|
||||
"""
|
||||
Verify idf_build_add_post_elf_dependency(<elf_filename> <dep_target>) adds a post-ELF step
|
||||
prior to BIN generation.
|
||||
"""
|
||||
cmake_snippet = '\n'.join(
|
||||
[
|
||||
'idf_build_get_property(elf_target EXECUTABLE GENERATOR_EXPRESSION)',
|
||||
'add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/postelf_file"',
|
||||
' COMMAND ${CMAKE_COMMAND} -E sleep 1',
|
||||
' COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/postelf_file"',
|
||||
' DEPENDS "$<TARGET_FILE:$<GENEX_EVAL:${elf_target}>>")',
|
||||
'add_custom_target(my_postelf DEPENDS "${CMAKE_BINARY_DIR}/postelf_file")',
|
||||
'idf_build_add_post_elf_dependency("${CMAKE_PROJECT_NAME}.elf" my_postelf)',
|
||||
]
|
||||
)
|
||||
|
||||
append_to_file('main/CMakeLists.txt', f'\n# post-elf test injection\n{cmake_snippet}\n')
|
||||
|
||||
run_cmake_and_build('-G', 'Ninja', str(test_app_copy))
|
||||
|
||||
elf_file = test_app_copy / 'build' / 'build_test_app.elf'
|
||||
postelf_file = test_app_copy / 'build' / 'postelf_file'
|
||||
bin_timestamp = test_app_copy / 'build' / '.bin_timestamp'
|
||||
|
||||
assert elf_file.exists(), 'ELF file must exist'
|
||||
assert postelf_file.exists(), 'post-elf file must be created'
|
||||
assert bin_timestamp.exists(), 'bin timestamp must exist'
|
||||
|
||||
snap = get_snapshot([str(elf_file), str(postelf_file), str(bin_timestamp)])
|
||||
mtimes = dict(snap.info)
|
||||
assert mtimes[str(postelf_file)] > mtimes[str(elf_file)], 'post-ELF file must be created after ELF file'
|
||||
assert mtimes[str(bin_timestamp)] > mtimes[str(postelf_file)], (
|
||||
'Binary generation must occur after post-ELF dependency'
|
||||
)
|
Reference in New Issue
Block a user