Merge branch 'feat/cmakev2_size_targets_v6.0' into 'release/v6.0'

fix(cmakev2): add size report targets (v6.0)

See merge request espressif/esp-idf!43546
This commit is contained in:
Roland Dobai
2025-11-20 13:54:22 +01:00
6 changed files with 250 additions and 84 deletions
+72 -22
View File
@@ -37,6 +37,10 @@ function(idf_build_set_property property value)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if("${property}" STREQUAL MINIMAL_BUILD)
idf_warn("Build property 'MINIMAL_BUILD' is obsolete and will be ignored")
endif()
set(append)
if(ARG_APPEND)
set(append APPEND)
@@ -503,15 +507,27 @@ endfunction()
Optional ``executable`` suffix.
*MAPFILE_TARGET[in,opt]*
Name of the target for the map file. If provided, the link map file is
generated for the specified executable, and the ``MAPFILE_TARGET``
target name is created for it. The ``MAPFILE_PATH`` property with the
link map file path is added to the ``MAPFILE_TARGET`` target. This can
be used for other targets that depend on the link map file. The link map file
is not generated on Darwin host, so the target ``MAPFILE_TARGET`` may not
be created if link map file is not generated.
Create a new executable target using the name specified in the
``executable`` argument, and link it to the library created with the
component names provided in the ``COMPONENTS`` option. If the
``COMPONENTS`` option is not set, all discovered components are added to
the library. Optinaly set the executable name and suffix.
the library. Optionally set the executable name and suffix. The executable
library target name is added to the ``LIBRARY_INTERFACE`` executable
property.
#]]
function(idf_build_executable executable)
set(options)
set(one_value NAME SUFFIX)
set(one_value NAME SUFFIX MAPFILE_TARGET)
set(multi_value COMPONENTS)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
@@ -534,15 +550,29 @@ function(idf_build_executable executable)
endif()
add_executable(${executable} "${executable_src}")
if(ARG_NAME)
set_target_properties(${executable} PROPERTIES OUTPUT_NAME ${ARG_NAME})
endif()
set_target_properties(${executable} PROPERTIES OUTPUT_NAME ${ARG_NAME})
if(ARG_SUFFIX)
set_target_properties(${executable} PROPERTIES SUFFIX ${ARG_SUFFIX})
endif()
target_link_libraries(${executable} PRIVATE ${library})
idf_build_get_property(linker_type LINKER_TYPE)
if(ARG_MAPFILE_TARGET AND "${linker_type}" STREQUAL "GNU")
set(mapfile "${CMAKE_BINARY_DIR}/${ARG_NAME}.map")
target_link_options(${executable} PRIVATE "LINKER:--Map=${mapfile}")
add_custom_command(
OUTPUT "${mapfile}"
DEPENDS ${executable}
)
add_custom_target(${ARG_MAPFILE_TARGET}
DEPENDS "${mapfile}"
)
set_target_properties(${ARG_MAPFILE_TARGET} PROPERTIES MAPFILE_PATH ${mapfile})
endif()
set_target_properties(${executable} PROPERTIES LIBRARY_INTERFACE ${library})
endfunction()
#[[
@@ -647,28 +677,34 @@ endfunction()
.. code-block:: cmake
idf_build_generate_metadata(<executable>
idf_build_generate_metadata(<binary>
[FILE <file>])
*executable[in]*
*binary[in]*
Executable target for which to generate a metadata file.
Binary target for which to generate a metadata file.
*OUTPUT_FILE[in,opt]*
Optional output file path for storing the metadata. If not provided,
the default path ``<build>/project_description.json`` is used.
Generate metadata for the specified ``executable`` and store it in the
Generate metadata for the specified ``binary`` and store it in the
specified ``FILE``. If no ``FILE`` is provided, the default location
``<build>/project_description.json`` will be used.
#]]
function(idf_build_generate_metadata executable)
function(idf_build_generate_metadata binary)
set(options)
set(one_value OUTPUT_FILE)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
# The EXECUTABLE_TARGET property is set by the idf_build_binary or
# the idf_sign_binary function.
get_target_property(executable "${binary}" EXECUTABLE_TARGET)
if(NOT executable)
idf_die("Binary target '${binary}' is missing 'EXECUTABLE_TARGET' property.")
endif()
__get_executable_library_or_die(TARGET "${executable}" OUTPUT library)
idf_build_get_property(PROJECT_NAME PROJECT_NAME)
@@ -679,9 +715,13 @@ function(idf_build_generate_metadata executable)
idf_build_get_property(SDKCONFIG SDKCONFIG)
idf_build_get_property(SDKCONFIG_DEFAULTS SDKCONFIG_DEFAULTS)
set(PROJECT_EXECUTABLE "$<TARGET_FILE_NAME:${executable}>")
# The PROJECT_BIN executable property must be set by the idf_build_binary
# function.
get_target_property(PROJECT_BIN "${executable}" EXECUTABLE_BINARY)
# The BINARY_PATH property is set by the idf_build_binary or
# the idf_sign_binary function.
get_target_property(binary_path ${binary} BINARY_PATH)
if(NOT binary_path)
idf_die("Binary target '${binary}' is missing 'BINARY_PATH' property.")
endif()
get_filename_component(PROJECT_BIN "${binary_path}" NAME)
if(NOT PROJECT_BIN)
set(PROJECT_BIN "")
endif()
@@ -767,8 +807,9 @@ endfunction()
Create a binary image for the specified ``executable`` target and save it
in the file specified with the ``OUTPUT_FILE`` option. A custom target
named ``TARGET`` will be created for the generated binary image. The path
of the generated binary image will be also stored in the ``BINARY_PATH``
property of the ``TARGET``.
of the generated binary image will be stored in the ``BINARY_PATH``
property and the executable target in the ``EXECUTABLE_TARGET`` property of
the ``TARGET``.
#]]
function(idf_build_binary executable)
set(options)
@@ -828,13 +869,13 @@ function(idf_build_binary executable)
# Create a custom target to generate the binary file
add_custom_target(${ARG_TARGET} DEPENDS "${ARG_OUTPUT_FILE}")
# The EXECUTABLE_BINARY property is used by idf_build_generate_metadata to
# store the name of the binary image.
set_target_properties(${executable} PROPERTIES EXECUTABLE_BINARY ${binary_name})
# Store the path of the binary file in the BINARY_PATH property of the
# custom binary target, which is used by the idf_flash_binary.
set_target_properties(${ARG_TARGET} PROPERTIES BINARY_PATH ${ARG_OUTPUT_FILE})
# Store executable target name in the EXECUTABLE_TARGET property. This is used
# by the idf_build_generate_metadata function.
set_target_properties(${ARG_TARGET} PROPERTIES EXECUTABLE_TARGET ${executable})
endfunction()
#[[
@@ -870,9 +911,10 @@ endfunction()
Sign binary image specified by ``binary`` target with ``KEYFILE`` and save
it in the file specified with the `OUTPUT_FILE` option. A custom target
named ``TARGET`` will be created for the signed binary image. The path of
the signed binary image will be also stored in the ``BINARY_PATH`` property
of the ``TARGET``.
named ``TARGET`` will be created for the signed binary image. The path of
the signed binary image will be stored in the ``BINARY_PATH`` property and
the executable target in the ``EXECUTABLE_TARGET`` property of the
``TARGET``.
#]]
function(idf_sign_binary binary)
set(options)
@@ -936,6 +978,14 @@ function(idf_sign_binary binary)
# Store the path of the binary file in the BINARY_PATH property of the
# custom signed binary target, which is used by the idf_flash_binary.
set_target_properties(${ARG_TARGET} PROPERTIES BINARY_PATH ${ARG_OUTPUT_FILE})
# Store executable target name in the EXECUTABLE_TARGET property. This is used
# by the idf_build_generate_metadata function.
get_target_property(executable "${binary}" EXECUTABLE_TARGET)
if(NOT executable)
idf_die("Binary target '${binary}' is missing 'EXECUTABLE_TARGET' property.")
endif()
set_target_properties(${ARG_TARGET} PROPERTIES EXECUTABLE_TARGET ${executable})
endfunction()
#[[
+1
View File
@@ -36,6 +36,7 @@ include(compat)
include(ldgen)
include(dfu)
include(uf2)
include(size)
include(GetGitRevisionDescription)
# For backward compatibility, since externalproject_add is used by
# project_include.cmake in the bootloader component. The ExternalProject
+57 -45
View File
@@ -90,6 +90,21 @@ function(__init_project_configuration)
idf_build_get_property(project_dir PROJECT_DIR)
idf_build_get_property(project_name PROJECT_NAME)
# Set the LINKER_TYPE build property. Different linkers may have varying
# options, so it's important to identify the linker type to configure the
# options correctly. Currently, LINKER_TYPE is used to set the appropriate
# linker options for linking the entire archive, which differs between the
# GNU and Apple linkers when building on the host.
if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
# Compiling for the host, and the host is macOS, so the linker is Darwin LD.
# Note, when adding support for Clang and LLD based toolchain this check will
# need to be modified.
set(linker_type "Darwin")
else()
set(linker_type "GNU")
endif()
idf_build_set_property(LINKER_TYPE "${linker_type}")
list(APPEND compile_definitions "_GLIBCXX_USE_POSIX_SEMAPHORE" # These two lines enable libstd++ to use
"_GLIBCXX_HAVE_POSIX_SEMAPHORE" # posix-semaphores from components/pthread
"_GNU_SOURCE")
@@ -407,16 +422,13 @@ function(__init_project_configuration)
list(APPEND link_options "-specs=picolibc.specs")
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
set(mapfile "${build_dir}/${project_name}.map")
if("${linker_type}" STREQUAL "GNU")
set(target_upper "${idf_target}")
string(TOUPPER ${target_upper} target_upper)
# Add cross-reference table to the map file
list(APPEND link_options "-Wl,--cref")
# Add this symbol as a hint for esp_idf_size to guess the target name
list(APPEND link_options "-Wl,--defsym=IDF_TARGET_${target_upper}=0")
# Enable map file output
list(APPEND link_options "-Wl,--Map=${mapfile}")
# Check if linker supports --no-warn-rwx-segments
execute_process(COMMAND ${CMAKE_LINKER} "--no-warn-rwx-segments" "--version"
RESULT_VARIABLE result
@@ -486,21 +498,6 @@ function(__init_project_configuration)
idf_build_set_property(ASM_COMPILE_OPTIONS "${asm_compile_options}" APPEND)
idf_build_set_property(COMPILE_DEFINITIONS "${compile_definitions}" APPEND)
idf_build_set_property(LINK_OPTIONS "${link_options}" APPEND)
# Set the LINKER_TYPE build property. Different linkers may have varying
# options, so it's important to identify the linker type to configure the
# options correctly. Currently, LINKER_TYPE is used to set the appropriate
# linker options for linking the entire archive, which differs between the
# GNU and Apple linkers when building on the host.
if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
# Compiling for the host, and the host is macOS, so the linker is Darwin LD.
# Note, when adding support for Clang and LLD based toolchain this check will
# need to be modified.
set(linker_type "Darwin")
else()
set(linker_type "GNU")
endif()
idf_build_set_property(LINKER_TYPE "${linker_type}")
endfunction()
#[[
@@ -599,6 +596,7 @@ macro(idf_project_init)
idf_die("sdkconfig.cmake file not found.")
endif()
include("${sdkconfig_cmake}")
unset(sdkconfig_cmake)
# Initialize the target architecture based on the configuration
# Ensure this is done after including the sdkconfig.
@@ -639,6 +637,7 @@ macro(idf_project_init)
idf_component_include("${component_name}")
endforeach()
endif()
unset(include_all_components)
idf_build_set_property(__PROJECT_INITIALIZED YES)
endif()
@@ -694,26 +693,22 @@ function(idf_build_generate_flasher_args)
INPUT "${build_dir}/flasher_args.json.in")
endfunction()
#[[api
.. cmakev2:macro:: idf_project_default
#[[
.. cmakev2:macro:: __project_default
.. code-block:: cmake
idf_project_default()
__project_default()
Create a default project executable based on the main component and its
transitive dependencies. The executable name is derived from the
``PROJECT_NAME`` variable, which by default uses the ``CMAKE_PROJECT_NAME``
value specified in the CMake's ``project()`` call.
Generate the binary image for the executable, signed or unsigned based on
the configuration, and add flash targets for it.
Helper function implementing the main idf_project_default macro
functionality, preventing global variable scope pollution.
#]]
macro(idf_project_default)
idf_project_init()
function(__project_default)
idf_build_get_property(build_dir BUILD_DIR)
idf_build_get_property(executable PROJECT_NAME)
idf_build_executable("${executable}" COMPONENTS main SUFFIX ".elf")
idf_build_executable("${executable}"
COMPONENTS main
MAPFILE_TARGET "${executable}_mapfile")
if(CONFIG_APP_BUILD_GENERATE_BINARIES)
# Is it possible to have a configuration where
@@ -734,6 +729,7 @@ macro(idf_project_default)
TARGET app-flash
NAME "app"
FLASH)
idf_build_generate_metadata("${executable}_binary_signed")
else()
idf_build_binary("${executable}"
OUTPUT_FILE "${build_dir}/${executable}.bin"
@@ -750,16 +746,7 @@ macro(idf_project_default)
idf_create_dfu("${executable}_binary"
TARGET dfu)
endif()
# FIXME: Dependencies should be specified within the components, not in the
# build system.
if(CONFIG_APP_BUILD_TYPE_APP_2NDBOOT)
add_dependencies(flash "partition_table_bin")
endif()
if(CONFIG_APP_BUILD_BOOTLOADER)
add_dependencies(flash "bootloader")
idf_build_generate_metadata("${executable}_binary")
endif()
idf_build_generate_flasher_args()
@@ -779,8 +766,33 @@ macro(idf_project_default)
TARGET uf2-app
APP_ONLY)
idf_build_generate_metadata("${executable}")
if(TARGET "${executable}_mapfile")
idf_create_size_report("${executable}_mapfile"
TARGET size)
endif()
endfunction()
unset(build_dir)
unset(executable)
#[[api
.. cmakev2:macro:: idf_project_default
.. code-block:: cmake
idf_project_default()
Create a default project executable based on the main component and its
transitive dependencies. The executable name is derived from the
``PROJECT_NAME`` variable, which by default uses the ``CMAKE_PROJECT_NAME``
value specified in the CMake's ``project()`` call.
Generate the binary image for the executable, signed or unsigned based on
the configuration, and add flash targets for it.
#]]
macro(idf_project_default)
idf_project_init()
# Only the idf_project_init macro needs be called within the global scope,
# as it includes the project_include.cmake files and the cmake version of
# the configuration. The remaining functionality of the idf_project_default
# macro is implemented in a __project_default helper function to avoid
# polluting the global variable space.
__project_default()
endmacro()
+83
View File
@@ -0,0 +1,83 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#[[
.. cmakev2:function:: idf_create_size_report
.. code-block:: cmake
idf_create_size_report(<mapfile>
TARGET <target>)
*mapfile[in]*
The mapfile target generated by the idf_build_executable function.
*TARGET[in]*
The base name for the size report targets to be created. In addition to
the default size report, which will be available under the target name
specified in the ``TARGET`` option, two more detailed targets,
"<target>-files" and "<target>-components", will also be created.
Create size report targets for the specified ``mapfile`` target. The
``TARGET`` option specifies the name of the default size report target, but
two more targets with detailed reports are also created. For example, if
``TARGET`` is set to "size," three targets "size", "size-files", and
"size-components" will be created.
#]]
function(idf_create_size_report mapfile_target)
set(options)
set(one_value TARGET)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_TARGET)
idf_die("TARGET option is required")
endif()
get_target_property(mapfile "${mapfile_target}" MAPFILE_PATH)
if(NOT mapfile)
idf_die("Mapfile target '${mapfile_target}' is missing 'MAPFILE_PATH' property.")
endif()
idf_build_get_property(idf_path IDF_PATH)
idf_build_get_property(python PYTHON)
set(idf_size ${python} -m esp_idf_size)
add_custom_target(${ARG_TARGET}
COMMAND ${CMAKE_COMMAND}
-D "IDF_SIZE_TOOL=${idf_size}"
-D "MAP_FILE=${mapfile}"
-D "OUTPUT_JSON=${OUTPUT_JSON}"
-P "${idf_path}/tools/cmake/run_size_tool.cmake"
DEPENDS ${mapfile_target}
USES_TERMINAL
VERBATIM
)
add_custom_target(${ARG_TARGET}-files
COMMAND ${CMAKE_COMMAND}
-D "IDF_SIZE_TOOL=${idf_size}"
-D "IDF_SIZE_MODE=--files"
-D "MAP_FILE=${mapfile}"
-D "OUTPUT_JSON=${OUTPUT_JSON}"
-P "${idf_path}/tools/cmake/run_size_tool.cmake"
DEPENDS ${mapfile_target}
USES_TERMINAL
VERBATIM
)
add_custom_target(${ARG_TARGET}-components
COMMAND ${CMAKE_COMMAND}
-D "IDF_SIZE_TOOL=${idf_size}"
-D "IDF_SIZE_MODE=--archives"
-D "MAP_FILE=${mapfile}"
-D "OUTPUT_JSON=${OUTPUT_JSON}"
-P "${idf_path}/tools/cmake/run_size_tool.cmake"
DEPENDS ${mapfile_target}
USES_TERMINAL
VERBATIM
)
endfunction()
+16 -4
View File
@@ -219,7 +219,8 @@ endfunction()
# idf.py confserver-fatfs
function(test_executable)
idf_build_executable(fatfs_example
COMPONENTS fatfs_example)
COMPONENTS fatfs_example
MAPFILE_TARGET fatfs_example_mapfile)
idf_build_binary(fatfs_example
TARGET fatfs_example_bin
OUTPUT_FILE fatfs_example.bin)
@@ -229,14 +230,20 @@ function(test_executable)
NAME fatfs_example)
idf_create_menuconfig(fatfs_example
TARGET menuconfig-fatfs)
idf_build_generate_metadata(fatfs_example
idf_build_generate_metadata(fatfs_example_bin
OUTPUT_FILE project_description_fatfs.json)
idf_create_confserver(fatfs_example
TARGET confserver-fatfs)
if(TARGET fatfs_example_mapfile)
idf_create_size_report(fatfs_example_mapfile
TARGET fatfs-size)
endif()
idf_build_executable(hello_world_example
COMPONENTS hello_world_example)
COMPONENTS hello_world_example
MAPFILE_TARGET hello_world_example_mapfile)
idf_build_binary(hello_world_example
TARGET hello_world_example_bin
OUTPUT_FILE hello_world_example.bin)
@@ -246,10 +253,15 @@ function(test_executable)
NAME hello_world_example)
idf_create_menuconfig(hello_world_example
TARGET menuconfig-hello_world)
idf_build_generate_metadata(hello_world_example
idf_build_generate_metadata(hello_world_example_bin
OUTPUT_FILE project_description_hello_world.json)
idf_create_confserver(hello_world_example
TARGET confserver-hello_world)
if(TARGET hello_world_example_mapfile)
idf_create_size_report(hello_world_example_mapfile
TARGET hello-size)
endif()
endfunction()
# Run tests
+21 -13
View File
@@ -981,11 +981,12 @@ endfunction()
Output variable to store the library interface target linked to the
executable.
Search for the library interface target created with the
idf_build_library() function and linked to the executable, examine the
LINK_LIBRARIES for the executable and the LIBRARY_INTERFACES build
property, which stores all library interface targets created by the
idf_build_library() function.
Search for the library interface target in the LIBRARY_INTERFACE executable
property. If not found, search for the library interface target created with
the idf_build_library() function and linked to the executable. Examine the
LINK_LIBRARIES for the executable and the LIBRARY_INTERFACES build property,
which stores all library interface targets created by the idf_build_library()
function.
#]]
function(__get_executable_library_or_die)
set(options)
@@ -1007,15 +1008,22 @@ function(__get_executable_library_or_die)
"but an 'EXECUTABLE' target type is expected.")
endif()
set(library NOTFOUND)
get_target_property(targets "${ARG_TARGET}" LINK_LIBRARIES)
idf_build_get_property(libraries LIBRARY_INTERFACES)
foreach(target IN LISTS targets)
if("${target}" IN_LIST libraries)
set(library "${target}")
break()
endif()
endforeach()
get_target_property(library "${ARG_TARGET}" LIBRARY_INTERFACE)
if(NOT "${library}" IN_LIST libraries)
# The library interface is not stored in the LIBRARY_INTERFACE
# executable property, so the executable was not created by the
# idf_build_executable function. Try searching for the library
# interface in the LINK_LIBRARIES property.
set(library NOTFOUND)
get_target_property(targets "${ARG_TARGET}" LINK_LIBRARIES)
foreach(target IN LISTS targets)
if("${target}" IN_LIST libraries)
set(library "${target}")
break()
endif()
endforeach()
endif()
if("${library}" STREQUAL "NOTFOUND")
idf_die("No library interface target linked to the '${ARG_TARGET}' executable")