mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
6278ca37f9
In some instances, the COMPONENTS_DISCOVERED build property is used to walk through the component list and obtain component properties, such as during configuration preparation or the inclusion of project_include files. Since we know the component interfaces from the COMPONENT_INTERFACES build property, we can switch to the faster `__idf_component_get_property_unchecked` method to obtain component properties in these cases. This change reduces the reconfiguration time by 0.4 seconds on my computer. Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
1264 lines
47 KiB
CMake
1264 lines
47 KiB
CMake
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
include_guard(GLOBAL)
|
|
|
|
include(utilities)
|
|
include(CheckCCompilerFlag)
|
|
include(CheckCXXCompilerFlag)
|
|
include(component_validation)
|
|
|
|
#[[api
|
|
.. cmakev2:function:: idf_build_set_property
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_build_set_property(<property> <value> [APPEND])
|
|
|
|
*property[in]*
|
|
|
|
Property name.
|
|
|
|
*value[in]*
|
|
|
|
Property value.
|
|
|
|
*APPEND*
|
|
|
|
Append the value to the property's current value instead of replacing
|
|
it.
|
|
|
|
Set the value of the specified property related to the ESP-IDF build. The
|
|
property is also added to the internal list of build properties if it isn't
|
|
already there.
|
|
#]]
|
|
function(idf_build_set_property property value)
|
|
set(options APPEND)
|
|
set(one_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)
|
|
endif()
|
|
|
|
__set_property(TARGET idf_build_properties
|
|
PROPERTY "${property}"
|
|
PROPERTIES BUILD_PROPERTIES
|
|
VALUE "${value}"
|
|
${append})
|
|
endfunction()
|
|
|
|
#[[api
|
|
.. cmakev2:function:: idf_build_get_property
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_build_get_property(<var> <property> [GENERATOR_EXPRESSION])
|
|
|
|
*variable[out]*
|
|
|
|
Variable to store the value in.
|
|
|
|
*property[in]*
|
|
|
|
Property name to get the value of.
|
|
|
|
*GENERATOR_EXPRESSION[in]*
|
|
|
|
Obtain the generator expression for the property rather than the actual
|
|
value.
|
|
|
|
Get the value of the specified property related to the ESP-IDF build.
|
|
#]]
|
|
function(idf_build_get_property variable property)
|
|
set(options GENERATOR_EXPRESSION)
|
|
set(one_value)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if("${property}" STREQUAL BUILD_COMPONENTS)
|
|
idf_die("Build property 'BUILD_COMPONENTS' is not supported")
|
|
endif()
|
|
|
|
set(genexpr)
|
|
if(ARG_GENERATOR_EXPRESSION)
|
|
set(genexpr GENERATOR_EXPRESSION)
|
|
endif()
|
|
|
|
__get_property(TARGET idf_build_properties
|
|
PROPERTY "${property}"
|
|
OUTPUT value
|
|
${genexpr})
|
|
set(${variable} ${value} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__dump_build_properties()
|
|
|
|
Dump all build properties.
|
|
#]]
|
|
function(__dump_build_properties)
|
|
idf_build_get_property(properties BUILD_PROPERTIES)
|
|
idf_msg("build properties: ${properties}")
|
|
foreach(property IN LISTS properties)
|
|
idf_build_get_property(value ${property})
|
|
idf_msg(" ${property}: ${value}")
|
|
endforeach()
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_library_interface_or_die(LIBRARY <library>
|
|
OUTPUT <variable>)
|
|
|
|
*LIBRARY[in]*
|
|
|
|
Library interface or alias.
|
|
|
|
*OUTPUT[out]*
|
|
|
|
Output variable to store the library interface.
|
|
|
|
Verify that "LIBRARY" is a known interface created by ``idf_build_library``
|
|
or its alias. If it is, return the library interface; otherwise, terminate
|
|
the build process.
|
|
#]]
|
|
function(__get_library_interface_or_die)
|
|
set(options)
|
|
set(one_value LIBRARY OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_LIBRARY)
|
|
idf_die("LIBRARY option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
__get_real_target(TARGET ${ARG_LIBRARY} OUTPUT library_interface)
|
|
idf_build_get_property(library_interfaces LIBRARY_INTERFACES)
|
|
|
|
if(NOT "${library_interface}" IN_LIST library_interfaces)
|
|
idf_die("Library interface '${ARG_LIBRARY}' does not exist")
|
|
endif()
|
|
set(${ARG_OUTPUT} ${library_interface} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_library_set_property
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_library_set_property(<library> <property> <value> [APPEND])
|
|
|
|
*library[in]*
|
|
|
|
Library interface target or alias.
|
|
|
|
*property[in]*
|
|
|
|
Property name.
|
|
|
|
*value[in]*
|
|
|
|
Property value.
|
|
|
|
*APPEND*
|
|
|
|
Append the value to the property's current value instead of replacing
|
|
it.
|
|
|
|
Set the value of the specified library property. The property is also added
|
|
to the internal list of library properties if it isn't already there.
|
|
#]]
|
|
function(idf_library_set_property library property value)
|
|
set(options APPEND)
|
|
set(one_value)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
set(append)
|
|
if(ARG_APPEND)
|
|
set(append APPEND)
|
|
endif()
|
|
|
|
__get_library_interface_or_die(LIBRARY "${library}" OUTPUT library_interface)
|
|
__set_property(TARGET "${library_interface}"
|
|
PROPERTY "${property}"
|
|
PROPERTIES LIBRARY_PROPERTIES
|
|
VALUE "${value}"
|
|
${append})
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_library_get_property
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_library_get_property(<variable> <library> <property> [GENERATOR_EXPRESSION])
|
|
|
|
*variable[out]*
|
|
|
|
Variable to store the value in.
|
|
|
|
*library[in]*
|
|
|
|
Library interface target or alias.
|
|
|
|
*property[in]*
|
|
|
|
Property name to get the value of.
|
|
|
|
*GENERATOR_EXPRESSION*
|
|
|
|
Obtain the generator expression for the property rather than the actual
|
|
value.
|
|
|
|
Retrieve the value of the specified library property.
|
|
#]]
|
|
function(idf_library_get_property variable library property)
|
|
set(options GENERATOR_EXPRESSION)
|
|
set(one_value)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
set(genexpr)
|
|
if(ARG_GENERATOR_EXPRESSION)
|
|
set(genexpr GENERATOR_EXPRESSION)
|
|
endif()
|
|
|
|
__get_library_interface_or_die(LIBRARY "${library}" OUTPUT library_interface)
|
|
__get_property(TARGET "${library_interface}"
|
|
PROPERTY "${property}"
|
|
OUTPUT value
|
|
${genexpr})
|
|
set(${variable} ${value} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__dump_library_properties(<libraries>)
|
|
|
|
*libraries*
|
|
|
|
List of library interfaces whose properties should be displayed.
|
|
|
|
Dump all properties for the libraries listed in ``<libraries>``.
|
|
#]]
|
|
function(__dump_library_properties libraries)
|
|
foreach(library IN LISTS libraries)
|
|
idf_library_get_property(properties "${library}" LIBRARY_PROPERTIES)
|
|
idf_msg("library '${library}' properties: ${properties}")
|
|
foreach(property IN LISTS properties)
|
|
idf_library_get_property(value "${library}" "${property}")
|
|
idf_msg(" ${property}: ${value}")
|
|
endforeach()
|
|
endforeach()
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_build_library
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_build_library(<library>
|
|
[COMPONENTS <component>...])
|
|
|
|
*library[in]*
|
|
|
|
Name of the library interface to be created.
|
|
|
|
*COMPONENTS[in,opt]*
|
|
|
|
List of component names to add to the library.
|
|
|
|
Create a new library interface target with the name specified in the
|
|
``library`` option and link component targets to it based on the component
|
|
names provided in the ``COMPONENTS`` option. If ``COMPONENTS`` option is
|
|
not set, link component targets of all discovered components.
|
|
|
|
List of library properties
|
|
|
|
LIBRARY_COMPONENTS
|
|
List of component as specified by the ``COMPONENTS`` option.
|
|
|
|
LIBRARY_COMPONENTS_LINKED
|
|
List of component names linked to the library based on recursive evaluations
|
|
of the INTERFACE_LINK_LIBRARIES and LINK_LIBRARIES target properties.
|
|
|
|
LIBRARY_COMPONENT_INTERFACES_LINKED
|
|
List of component interfaces linked to the library based on recursive
|
|
evaluations of the INTERFACE_LINK_LIBRARIES and LINK_LIBRARIES target
|
|
properties.
|
|
#]]
|
|
function(idf_build_library library)
|
|
set(options)
|
|
set(one_value)
|
|
set(multi_value COMPONENTS)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
idf_build_get_property(project_initialized __PROJECT_INITIALIZED)
|
|
if(NOT project_initialized)
|
|
idf_die("The IDF project is not initialized. The 'idf_project_init()' must be called first.")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_COMPONENTS)
|
|
# The library should include all discovered components.
|
|
idf_build_get_property(component_names COMPONENTS_DISCOVERED)
|
|
set(ARG_COMPONENTS "${component_names}")
|
|
endif()
|
|
|
|
# Create library interface.
|
|
add_library("${library}" INTERFACE)
|
|
idf_build_set_property(LIBRARY_INTERFACES "${library}" APPEND)
|
|
idf_library_set_property("${library}" LIBRARY_COMPONENTS "${ARG_COMPONENTS}")
|
|
|
|
# Add global include directories, such as the directory containing
|
|
# sdkconfig, to the library's include directories.
|
|
idf_build_get_property(include_directories INCLUDE_DIRECTORIES GENERATOR_EXPRESSION)
|
|
target_include_directories("${library}" INTERFACE "${include_directories}")
|
|
|
|
# Add link options.
|
|
idf_build_get_property(link_options LINK_OPTIONS)
|
|
target_link_options(${library} INTERFACE "${link_options}")
|
|
|
|
# Include the requested components and link their interface targets to the
|
|
# library.
|
|
foreach(component_name IN LISTS ARG_COMPONENTS)
|
|
idf_component_include("${component_name}")
|
|
idf_component_get_property(component_interface "${component_name}" COMPONENT_INTERFACE)
|
|
target_link_libraries("${library}" INTERFACE "${component_interface}")
|
|
endforeach()
|
|
|
|
# Get all targets transitively linked to the library interface target.
|
|
__get_target_dependencies(TARGET "${library}" OUTPUT dependencies)
|
|
|
|
# Identify the components linked to the library by looking at all targets
|
|
# that are transitively linked to the library and mapping these targets to
|
|
# components. Store the linked component name in LIBRARY_COMPONENTS_LINKED
|
|
# and component interface in LIBRARY_COMPONENT_INTERFACES_LINKED property.
|
|
foreach(dep IN LISTS dependencies)
|
|
__get_component_interface(COMPONENT "${dep}" OUTPUT component_interface)
|
|
if(NOT component_interface)
|
|
continue()
|
|
endif()
|
|
idf_library_get_property(component_interfaces_linked "${library}" LIBRARY_COMPONENT_INTERFACES_LINKED)
|
|
if("${component_interface}" IN_LIST component_interfaces_linked)
|
|
continue()
|
|
endif()
|
|
|
|
idf_component_get_property(component_name "${component_interface}" COMPONENT_NAME)
|
|
idf_library_set_property("${library}" LIBRARY_COMPONENTS_LINKED "${component_name}" APPEND)
|
|
idf_library_set_property("${library}" LIBRARY_COMPONENT_INTERFACES_LINKED "${component_interface}" APPEND)
|
|
endforeach()
|
|
|
|
# Collect linker fragment files from all components linked to the library
|
|
# interface and store them in the __LDGEN_FRAGMENT_FILES files. This
|
|
# property is used by ldgen to generate template-based linker scripts.
|
|
idf_library_get_property(component_interfaces_linked "${library}" LIBRARY_COMPONENT_INTERFACES_LINKED)
|
|
foreach(component_interface IN LISTS component_interfaces_linked)
|
|
idf_component_get_property(component_ldfragments "${component_interface}" LDFRAGMENTS)
|
|
idf_component_get_property(component_directory "${component_interface}" COMPONENT_DIR)
|
|
__get_absolute_paths(PATHS "${component_ldfragments}"
|
|
BASE_DIR "${component_directory}"
|
|
OUTPUT ldfragments)
|
|
idf_library_set_property("${library}" __LDGEN_FRAGMENT_FILES "${ldfragments}" APPEND)
|
|
endforeach()
|
|
|
|
# Collect the filenames of project component archives, which are considered
|
|
# mutable, in the __LDGEN_MUTABLE_LIBS library property. These libraries
|
|
# are placed by ldgen into a separate location in the linker script,
|
|
# enabling the fast reflashing feature.
|
|
idf_library_get_property(component_interfaces_linked "${library}" LIBRARY_COMPONENT_INTERFACES_LINKED)
|
|
foreach(component_interface IN LISTS component_interfaces_linked)
|
|
idf_component_get_property(component_source "${component_interface}" COMPONENT_SOURCE)
|
|
idf_component_get_property(component_target "${component_interface}" COMPONENT_TARGET)
|
|
idf_component_get_property(component_type "${component_interface}" COMPONENT_TYPE)
|
|
|
|
if("${component_type}" STREQUAL "CONFIG_ONLY")
|
|
# Configuration only component interface target without a library.
|
|
continue()
|
|
endif()
|
|
|
|
if(NOT "${component_source}" STREQUAL "project_components")
|
|
# Add only project components as mutable.
|
|
continue()
|
|
endif()
|
|
|
|
idf_library_set_property("${library}" __LDGEN_MUTABLE_LIBS
|
|
"$<TARGET_LINKER_FILE_NAME:${component_target}>" APPEND)
|
|
endforeach()
|
|
|
|
# Collect archive files from all targets linked to the library interface
|
|
# and store them in the __LDGEN_DEPENDS and __LDGEN_LIBRARIES library
|
|
# properties. These properties are used by ldgen to generate linker scripts
|
|
# from templates. The __LDGEN_LIBRARIES property contains a list of
|
|
# TARGET_FILE generator expressions for archive files.
|
|
foreach(dep IN LISTS dependencies)
|
|
if(NOT TARGET "${dep}")
|
|
continue()
|
|
endif()
|
|
|
|
get_target_property(type "${dep}" TYPE)
|
|
if("${type}" STREQUAL "INTERFACE_LIBRARY")
|
|
continue()
|
|
endif()
|
|
|
|
idf_library_get_property(ldgen_depends "${library}" __LDGEN_DEPENDS)
|
|
if(NOT "${dep}" IN_LIST ldgen_depends)
|
|
idf_library_set_property("${library}" __LDGEN_LIBRARIES "$<TARGET_FILE:${dep}>" APPEND)
|
|
idf_library_set_property("${library}" __LDGEN_DEPENDS ${dep} APPEND)
|
|
endif()
|
|
endforeach()
|
|
|
|
# Create a sanitized library interface name that can be used as a suffix
|
|
# for files and targets specific to the library.
|
|
string(MAKE_C_IDENTIFIER "_${library}" suffix)
|
|
|
|
idf_library_get_property(component_interfaces_linked "${library}" LIBRARY_COMPONENT_INTERFACES_LINKED)
|
|
foreach(component_interface IN LISTS component_interfaces_linked)
|
|
set(scripts)
|
|
idf_component_get_property(component_dir "${component_interface}" COMPONENT_DIR)
|
|
idf_component_get_property(component_build_dir "${component_interface}" COMPONENT_BUILD_DIR)
|
|
idf_component_get_property(preprocessed "${component_interface}" __LINKER_SCRIPTS_PREPROCESSED)
|
|
|
|
# Add linker scripts that do not require ldgen processing.
|
|
idf_component_get_property(scripts_static "${component_interface}" LINKER_SCRIPTS)
|
|
__get_absolute_paths(PATHS "${scripts_static}" BASE_DIR "${component_dir}" OUTPUT scripts_static_abs)
|
|
foreach(script IN LISTS scripts_static_abs)
|
|
if(script MATCHES "\\.in$")
|
|
# If the linker script file name ends with the ".in" extension,
|
|
# preprocess it with the C preprocessor.
|
|
get_filename_component(script_name "${script}" NAME_WLE)
|
|
set(script_out "${component_build_dir}/ld/${script_name}")
|
|
string(MAKE_C_IDENTIFIER "gen_${script_name}" script_target)
|
|
|
|
if(NOT preprocessed)
|
|
file(MAKE_DIRECTORY "${component_build_dir}/ld")
|
|
__preprocess_linker_script("${script}" "${script_out}")
|
|
# Add a custom target for the preprocessed script.
|
|
add_custom_target(${script_target} DEPENDS "${script_out}")
|
|
endif()
|
|
# Add dependency for the library interface to ensure the script is
|
|
# generated before linking.
|
|
add_dependencies("${library}" ${script_target})
|
|
list(APPEND scripts "${script_out}")
|
|
else()
|
|
list(APPEND scripts "${script}")
|
|
endif()
|
|
endforeach()
|
|
|
|
# Generate linker scripts from templates.
|
|
# LINKER_SCRIPTS_TEMPLATE and LINKER_SCRIPTS_GENERATED are parallel
|
|
# lists. The first holds the template linker script path, and the
|
|
# second holds the generated linker script path.
|
|
idf_component_get_property(template_scripts "${component_interface}" LINKER_SCRIPTS_TEMPLATE)
|
|
idf_component_get_property(generated_scripts "${component_interface}" LINKER_SCRIPTS_GENERATED)
|
|
|
|
foreach(template script IN ZIP_LISTS template_scripts generated_scripts)
|
|
if(template MATCHES "\\.in$")
|
|
# If the linker script file name ends with the ".in" extension,
|
|
# preprocess it with the C preprocessor.
|
|
get_filename_component(template_name "${template}" NAME)
|
|
set(template_out "${component_build_dir}/ld/${template_name}")
|
|
if(NOT preprocessed)
|
|
file(MAKE_DIRECTORY "${component_build_dir}/ld")
|
|
__preprocess_linker_script("${template}" "${template_out}")
|
|
endif()
|
|
set(template "${template_out}")
|
|
endif()
|
|
set(script "${script}${suffix}")
|
|
__ldgen_process_template(LIBRARY "${library}"
|
|
TEMPLATE "${template}"
|
|
SUFFIX "${suffix}"
|
|
OUTPUT "${script}")
|
|
list(APPEND scripts "${script}")
|
|
# Add a custom target for the generated script and include it as a
|
|
# dependency for the library interface to ensure the script is
|
|
# generated before linking.
|
|
get_filename_component(basename "${script}" NAME)
|
|
string(MAKE_C_IDENTIFIER "${basename}" basename)
|
|
add_custom_target(gen_${basename} DEPENDS "${script}")
|
|
add_dependencies("${library}" gen_${basename})
|
|
endforeach()
|
|
|
|
# The static scripts for the given component have already been
|
|
# preprocessed and are identical across all libraries. Therefore, there
|
|
# is no need to preprocess them multiple times. Set the component
|
|
# property to indicate that the linker scripts have already been
|
|
# preprocessed.
|
|
idf_component_set_property("${component_interface}" __LINKER_SCRIPTS_PREPROCESSED YES)
|
|
|
|
# Finally, add all preprocessed and ldgen-generated linker scripts to
|
|
# the library interface.
|
|
foreach(script IN LISTS scripts)
|
|
get_filename_component(script_dir "${script}" DIRECTORY)
|
|
get_filename_component(script_name "${script}" NAME)
|
|
# Add linker script directory to the linker search path.
|
|
target_link_directories("${library}" INTERFACE "${script_dir}")
|
|
# Add linker script to link. Regarding the usage of SHELL, see
|
|
# https://cmake.org/cmake/help/latest/command/target_link_options.html#option-de-duplication
|
|
target_link_options("${library}" INTERFACE "SHELL:-T ${script_name}")
|
|
# Add the linker script as a dependency to ensure the executable is
|
|
# re-linked if the script changes.
|
|
set_property(TARGET "${library}" APPEND PROPERTY INTERFACE_LINK_DEPENDS "${script}")
|
|
endforeach()
|
|
endforeach()
|
|
|
|
# Validate components linked to this library
|
|
__component_validation_run_checks(LIBRARY "${library}")
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_build_executable
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_build_executable(<executable>
|
|
[COMPONENTS <component>...]
|
|
[NAME <name>]
|
|
[SUFFIX <suffix>])
|
|
|
|
*executable[in]*
|
|
|
|
Name of the executable target to be created.
|
|
|
|
*COMPONENTS[in,opt]*
|
|
|
|
List of component names to add to the library.
|
|
|
|
*NAME[in,opt]*
|
|
|
|
Optional preferred generated binary name. If not provided, the
|
|
``executable`` name is used.
|
|
|
|
*SUFFIX[in,opt]*
|
|
|
|
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. 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 MAPFILE_TARGET)
|
|
set(multi_value COMPONENTS)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_NAME)
|
|
set(ARG_NAME "${executable}")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_SUFFIX)
|
|
set(ARG_SUFFIX ".elf")
|
|
endif()
|
|
|
|
idf_build_get_property(build_dir BUILD_DIR)
|
|
|
|
set(library "library_${executable}")
|
|
idf_build_library(${library} COMPONENTS "${ARG_COMPONENTS}")
|
|
|
|
set(executable_src ${CMAKE_BINARY_DIR}/executable_${executable}.c)
|
|
if(NOT EXISTS "${executable_src}")
|
|
file(TOUCH "${executable_src}")
|
|
endif()
|
|
add_executable(${executable} "${executable_src}")
|
|
|
|
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()
|
|
|
|
#[[
|
|
__get_components_metadata(COMPONENTS <component_interface>...
|
|
OUTPUT <variable>)
|
|
|
|
*COMPONENTS[in]*
|
|
|
|
List of component interfaces for which to generate metadata.
|
|
|
|
*OUTPUT[out]*
|
|
|
|
Output variable to store JSON metadata.
|
|
|
|
Generate metadata in JSON format from ``COMPONENTS`` and store it in the
|
|
``OUTPUT`` variable.
|
|
#]]
|
|
function(__get_components_metadata)
|
|
set(options)
|
|
set(one_value OUTPUT)
|
|
set(multi_value COMPONENTS)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_COMPONENTS)
|
|
idf_die("COMPONENTS option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
set(components_json "")
|
|
|
|
foreach(interface IN LISTS ARG_COMPONENTS)
|
|
__idf_component_get_property_unchecked(name ${interface} COMPONENT_NAME)
|
|
__idf_component_get_property_unchecked(target ${interface} COMPONENT_REAL_TARGET)
|
|
__idf_component_get_property_unchecked(alias ${interface} COMPONENT_ALIAS)
|
|
__idf_component_get_property_unchecked(prefix ${interface} __PREFIX)
|
|
__idf_component_get_property_unchecked(dir ${interface} COMPONENT_DIR)
|
|
__idf_component_get_property_unchecked(type ${interface} COMPONENT_TYPE)
|
|
__idf_component_get_property_unchecked(lib ${interface} COMPONENT_LIB)
|
|
__idf_component_get_property_unchecked(reqs ${interface} REQUIRES)
|
|
__idf_component_get_property_unchecked(include_dirs ${interface} INCLUDE_DIRS)
|
|
__idf_component_get_property_unchecked(priv_reqs ${interface} PRIV_REQUIRES)
|
|
__idf_component_get_property_unchecked(managed_reqs ${interface} MANAGED_REQUIRES)
|
|
__idf_component_get_property_unchecked(managed_priv_reqs ${interface} MANAGED_PRIV_REQUIRES)
|
|
__idf_component_get_property_unchecked(component_source ${interface} COMPONENT_SOURCE)
|
|
if("${type}" STREQUAL "LIBRARY")
|
|
set(file "$<TARGET_LINKER_FILE:${lib}>")
|
|
|
|
# The idf_component_register function is converting each source file path defined
|
|
# in SRCS into absolute one. But source files can be also added with cmake's
|
|
# target_sources and have relative paths. This is used for example in log
|
|
# component. Let's make sure all source files have absolute path.
|
|
set(sources "")
|
|
get_target_property(srcs ${lib} SOURCES)
|
|
foreach(src IN LISTS srcs)
|
|
get_filename_component(src "${src}" ABSOLUTE BASE_DIR "${dir}")
|
|
list(APPEND sources "${src}")
|
|
endforeach()
|
|
|
|
else()
|
|
set(file "")
|
|
set(sources "")
|
|
endif()
|
|
|
|
__make_json_list("${reqs}" OUTPUT reqs)
|
|
__make_json_list("${priv_reqs}" OUTPUT priv_reqs)
|
|
__make_json_list("${managed_reqs}" OUTPUT managed_reqs)
|
|
__make_json_list("${managed_priv_reqs}" OUTPUT managed_priv_reqs)
|
|
__make_json_list("${include_dirs}" OUTPUT include_dirs)
|
|
__make_json_list("${sources}" OUTPUT sources)
|
|
|
|
string(JOIN "\n" component_json
|
|
" \"${name}\": {"
|
|
" \"alias\": \"${alias}\","
|
|
" \"target\": \"${target}\","
|
|
" \"prefix\": \"${prefix}\","
|
|
" \"dir\": \"${dir}\","
|
|
" \"source\": \"${component_source}\","
|
|
" \"type\": \"${type}\","
|
|
" \"lib\": \"${lib}\","
|
|
" \"reqs\": ${reqs},"
|
|
" \"priv_reqs\": ${priv_reqs},"
|
|
" \"managed_reqs\": ${managed_reqs},"
|
|
" \"managed_priv_reqs\": ${managed_priv_reqs},"
|
|
" \"file\": \"${file}\","
|
|
" \"sources\": ${sources},"
|
|
" \"include_dirs\": ${include_dirs}"
|
|
" }"
|
|
)
|
|
string(CONFIGURE "${component_json}" component_json)
|
|
if(NOT "${components_json}" STREQUAL "")
|
|
string(APPEND components_json ",\n")
|
|
endif()
|
|
string(APPEND components_json "${component_json}")
|
|
endforeach()
|
|
string(PREPEND components_json "{\n")
|
|
string(APPEND components_json "\n }")
|
|
set(${ARG_OUTPUT} "${components_json}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_build_generate_metadata
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_build_generate_metadata(<binary>
|
|
[OUTPUT_FILE <file>])
|
|
|
|
*binary[in]*
|
|
|
|
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 ``binary`` and store it in the
|
|
specified ``OUTPUT_FILE``. If no ``OUTPUT_FILE`` is provided, the default
|
|
location ``<build>/project_description.json`` will be used.
|
|
#]]
|
|
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)
|
|
idf_build_get_property(PROJECT_VER PROJECT_VER)
|
|
idf_build_get_property(PROJECT_PATH PROJECT_DIR)
|
|
idf_build_get_property(IDF_PATH IDF_PATH)
|
|
idf_build_get_property(BUILD_DIR BUILD_DIR)
|
|
idf_build_get_property(SDKCONFIG SDKCONFIG)
|
|
idf_build_get_property(SDKCONFIG_DEFAULTS SDKCONFIG_DEFAULTS)
|
|
set(PROJECT_EXECUTABLE "$<TARGET_FILE_NAME:${executable}>")
|
|
# 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()
|
|
if(CONFIG_APP_BUILD_TYPE_RAM)
|
|
set(PROJECT_BUILD_TYPE ram_app)
|
|
else()
|
|
set(PROJECT_BUILD_TYPE flash_app)
|
|
endif()
|
|
idf_build_get_property(IDF_VER IDF_VER)
|
|
|
|
idf_build_get_property(common_component_reqs __COMPONENT_REQUIRES_COMMON)
|
|
list(SORT common_component_reqs)
|
|
__make_json_list("${common_component_reqs}" OUTPUT common_component_reqs_json)
|
|
|
|
idf_library_get_property(build_components "${library}" LIBRARY_COMPONENTS_LINKED)
|
|
idf_library_get_property(build_component_interfaces "${library}" LIBRARY_COMPONENT_INTERFACES_LINKED)
|
|
list(SORT build_components)
|
|
list(SORT build_component_interfaces)
|
|
__make_json_list("${build_components}" OUTPUT build_components_json)
|
|
|
|
set(build_component_paths)
|
|
set(COMPONENT_KCONFIGS)
|
|
set(COMPONENT_KCONFIGS_PROJBUILD)
|
|
foreach(component_interface IN LISTS build_component_interfaces)
|
|
__idf_component_get_property_unchecked(component_dir "${component_interface}" COMPONENT_DIR)
|
|
__idf_component_get_property_unchecked(component_kconfig "${component_interface}" __KCONFIG)
|
|
__idf_component_get_property_unchecked(component_kconfig_projbuild "${component_interface}" __KCONFIG_PROJBUILD)
|
|
list(APPEND build_component_paths "${component_dir}")
|
|
if(component_kconfig)
|
|
list(APPEND COMPONENT_KCONFIGS "${component_kconfig}")
|
|
endif()
|
|
if(component_kconfig_projbuild)
|
|
list(APPEND COMPONENT_KCONFIGS_PROJBUILD "${component_kconfig_projbuild}")
|
|
endif()
|
|
endforeach()
|
|
__make_json_list("${build_component_paths}" OUTPUT build_component_paths_json)
|
|
|
|
__get_components_metadata(COMPONENTS "${build_component_interfaces}" OUTPUT build_component_info_json)
|
|
|
|
idf_build_get_property(component_interfaces COMPONENT_INTERFACES)
|
|
__get_components_metadata(COMPONENTS "${component_interfaces}" OUTPUT all_component_info_json)
|
|
|
|
__generate_gdbinit()
|
|
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)
|
|
set(ARG_OUTPUT_FILE "${BUILD_DIR}/project_description.json")
|
|
endif()
|
|
|
|
get_filename_component(ARG_OUTPUT_FILE "${ARG_OUTPUT_FILE}" ABSOLUTE BASE_DIR "${BUILD_DIR}")
|
|
|
|
configure_file("${IDF_PATH}/tools/cmake/project_description.json.in" "${ARG_OUTPUT_FILE}.templ")
|
|
file(READ "${ARG_OUTPUT_FILE}.templ" project_description_json_templ)
|
|
file(REMOVE "${ARG_OUTPUT_FILE}.templ")
|
|
file(GENERATE OUTPUT "${ARG_OUTPUT_FILE}"
|
|
CONTENT "${project_description_json_templ}")
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_build_binary
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_build_binary(<executable>
|
|
TARGET <target>
|
|
OUTPUT_FILE <file>)
|
|
|
|
*executable[in]*
|
|
|
|
Executable target for which to generate a binary image file.
|
|
|
|
*TARGET[in]*
|
|
|
|
The name of the target that will be created for the generated binary
|
|
image.
|
|
|
|
*OUTPUT_FILE[in]*
|
|
|
|
Output file path for storing the binary image file.
|
|
|
|
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 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)
|
|
set(one_value OUTPUT_FILE TARGET)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT TARGET idf::esptool_py)
|
|
idf_die("The 'esptool_py' component is not available")
|
|
endif()
|
|
|
|
if(NOT CONFIG_APP_BUILD_GENERATE_BINARIES)
|
|
idf_die("Binary file generation is not enabled with 'CONFIG_APP_BUILD_GENERATE_BINARIES'")
|
|
endif()
|
|
|
|
if(NOT TARGET "${executable}")
|
|
idf_die("The executable '${executable}' is not a cmake target")
|
|
endif()
|
|
|
|
get_target_property(type "${executable}" TYPE)
|
|
if(NOT "${type}" STREQUAL "EXECUTABLE")
|
|
idf_die("The executable target '${executable}' is not of the EXECUTABLE type")
|
|
endif()
|
|
|
|
idf_build_get_property(build_dir BUILD_DIR)
|
|
|
|
get_target_property(executable_name ${executable} OUTPUT_NAME)
|
|
if(NOT executable_name)
|
|
set(executable_name "${executable}")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT_FILE)
|
|
idf_die("OUTPUT_FILE option is required")
|
|
endif()
|
|
|
|
idf_component_get_property(esptool_py_cmd esptool_py ESPTOOLPY_CMD)
|
|
idf_component_get_property(esptool_elf2image_args esptool_py ESPTOOL_PY_ELF2IMAGE_ARGS)
|
|
|
|
get_filename_component(binary_name "${ARG_OUTPUT_FILE}" NAME)
|
|
|
|
# Create a custom command and target to generate a binary from an ELF file.
|
|
add_custom_command(OUTPUT "${ARG_OUTPUT_FILE}"
|
|
COMMAND ${esptool_py_cmd} elf2image ${esptool_elf2image_args}
|
|
-o "${ARG_OUTPUT_FILE}" "$<TARGET_FILE:${executable}>"
|
|
COMMAND ${CMAKE_COMMAND} -E echo "Generated ${ARG_OUTPUT_FILE}"
|
|
COMMAND ${CMAKE_COMMAND} -E md5sum "${ARG_OUTPUT_FILE}" > "${ARG_OUTPUT_FILE}.md5sum"
|
|
DEPENDS ${executable}
|
|
VERBATIM
|
|
WORKING_DIRECTORY ${build_dir}
|
|
COMMENT "Generating binary image '${binary_name}' from executable '${executable_name}'"
|
|
)
|
|
|
|
# Create a custom target to generate the binary file
|
|
add_custom_target(${ARG_TARGET} DEPENDS "${ARG_OUTPUT_FILE}")
|
|
|
|
# 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()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_sign_binary
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_sign_binary(<binary>
|
|
TARGET <target>
|
|
OUTPUT_FILE <file>
|
|
[KEYFILE <file>])
|
|
|
|
*binary[in]*
|
|
|
|
Binary image target for which to generate a signed binary image file.
|
|
The ``binary`` target is created by the :cmakev2:ref:`idf_build_binary`
|
|
function.
|
|
|
|
*TARGET[in]*
|
|
|
|
The name of the target that will be created for the signed binary
|
|
image.
|
|
|
|
*OUTPUT_FILE[in]*
|
|
|
|
Output file path for storing the signed binary image file.
|
|
|
|
*KEYFILE[in,opt]*
|
|
|
|
Optional path to the key file that should be used for signing. If not
|
|
provided, the key file specified by the
|
|
``CONFIG_SECURE_BOOT_SIGNING_KEY`` configuration option will be used.
|
|
|
|
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 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)
|
|
set(one_value OUTPUT_FILE TARGET KEYFILE)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT TARGET idf::esptool_py)
|
|
idf_die("The 'esptool_py' component is not available")
|
|
endif()
|
|
|
|
if(NOT CONFIG_APP_BUILD_GENERATE_BINARIES)
|
|
idf_die("Binary file generation is not enabled with 'CONFIG_APP_BUILD_GENERATE_BINARIES'")
|
|
endif()
|
|
|
|
if(NOT CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
|
|
idf_die("Binary file signing is not enabled with 'CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES'")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT_FILE)
|
|
idf_die("OUTPUT_FILE option is required")
|
|
endif()
|
|
|
|
if(ARG_KEYFILE)
|
|
set(keyfle "${ARG_KEYFILE}")
|
|
else()
|
|
idf_build_get_property(project_dir PROJECT_DIR)
|
|
get_filename_component(keyfile "${CONFIG_SECURE_BOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR "${project_dir}")
|
|
endif()
|
|
|
|
if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
|
set(secure_boot_version "1")
|
|
elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME OR CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME)
|
|
set(secure_boot_version "2")
|
|
endif()
|
|
|
|
idf_component_get_property(espsecure_py_cmd esptool_py ESPSECUREPY_CMD)
|
|
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(binary_name "${binary_path}" NAME)
|
|
get_filename_component(signed_binary_name "${ARG_OUTPUT_FILE}" NAME)
|
|
get_filename_component(key_name "${keyfile}" NAME)
|
|
|
|
add_custom_command(OUTPUT "${ARG_OUTPUT_FILE}"
|
|
COMMAND ${espsecure_py_cmd} sign-data
|
|
--version ${secure_boot_version} --keyfile "${keyfile}"
|
|
-o "${ARG_OUTPUT_FILE}" "${binary_path}"
|
|
COMMAND ${CMAKE_COMMAND} -E md5sum "${ARG_OUTPUT_FILE}" > "${ARG_OUTPUT_FILE}.md5sum"
|
|
DEPENDS "${binary}"
|
|
VERBATIM
|
|
COMMENT "Signing '${binary_name}' with key '${key_name}' into '${signed_binary_name}'"
|
|
)
|
|
add_custom_target(${ARG_TARGET} DEPENDS "${ARG_OUTPUT_FILE}")
|
|
|
|
# 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()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_flash_binary
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_flash_binary(<binary>
|
|
TARGET <target>
|
|
[NAME <name>]
|
|
[FLASH])
|
|
|
|
*binary[in]*
|
|
|
|
Binary image target for which to create flash target. The ``binary``
|
|
target is created by the :cmakev2:ref:`idf_build_binary` function or
|
|
:cmakev2:ref:`idf_sign_binary`.
|
|
|
|
*TARGET[in]*
|
|
|
|
The name of the flash target that will be created for the binary image.
|
|
|
|
*NAME[in,opt]*
|
|
|
|
An optional name to be used as a prefix for the file containing
|
|
arguments for esptool, and as a logical name for the binary in
|
|
``flasher_args.json``. If not specified, the name of the ``TARGET`` is
|
|
used.
|
|
|
|
*FLASH[in,opt]*
|
|
|
|
If specified, the binary will also be added to the global ``flash``
|
|
target.
|
|
|
|
Create a new flash target for ``binary`` using the name specified in the
|
|
``TARGET`` option. The file path of the binary image should be stored in
|
|
the ``BINARY_PATH`` property of the ``binary`` target.
|
|
#]]
|
|
function(idf_flash_binary binary)
|
|
set(options FLASH)
|
|
set(one_value TARGET NAME)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT TARGET idf::esptool_py)
|
|
idf_die("The 'esptool_py' component is not available")
|
|
endif()
|
|
|
|
if(NOT CONFIG_APP_BUILD_GENERATE_BINARIES)
|
|
idf_die("Binary file generation is not enabled with 'CONFIG_APP_BUILD_GENERATE_BINARIES'")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_NAME)
|
|
set(ARG_NAME "${ARG_TARGET}")
|
|
endif()
|
|
|
|
idf_component_get_property(main_args esptool_py FLASH_ARGS)
|
|
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
|
|
|
|
partition_table_get_partition_info(offset "--partition-boot-default" "offset")
|
|
get_target_property(binary_path ${binary} BINARY_PATH)
|
|
if(NOT binary_path)
|
|
idf_die("Binary target '${binary}' is missing 'BINARY_PATH' property.")
|
|
endif()
|
|
|
|
idf_build_get_property(build_dir BUILD_DIR)
|
|
get_filename_component(binary_path "${binary_path}" ABSOLUTE BASE_DIR "${build_dir}")
|
|
|
|
esptool_py_flash_target("${ARG_TARGET}" "${main_args}" "${sub_args}"
|
|
FILENAME_PREFIX "${ARG_NAME}")
|
|
esptool_py_flash_target_image("${ARG_TARGET}" "${ARG_NAME}" "${offset}" "${binary_path}")
|
|
add_dependencies("${ARG_TARGET}" "${binary}")
|
|
|
|
if(ARG_FLASH)
|
|
esptool_py_flash_target_image(flash ${ARG_NAME} "${offset}" "${binary_path}")
|
|
add_dependencies(flash "${binary}")
|
|
endif()
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_check_binary_size
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_check_binary_size(<binary>)
|
|
|
|
*binary[in]*
|
|
|
|
Binary image target to which to add a partition size check. The
|
|
``binary`` target is created by the :cmakev2:ref:`idf_build_binary` or
|
|
:cmakev2:ref:`idf_sign_binary` function.
|
|
|
|
Ensure that the generated binary image fits within the smallest available
|
|
application partition. The file path of the binary image should be stored
|
|
in the ``BINARY_PATH`` property of the ``binary`` target.
|
|
#]]
|
|
function(idf_check_binary_size binary)
|
|
if(NOT CONFIG_APP_BUILD_TYPE_APP_2NDBOOT)
|
|
return()
|
|
endif()
|
|
|
|
get_target_property(binary_path ${binary} BINARY_PATH)
|
|
if(NOT binary_path)
|
|
idf_die("Binary target '${binary}' is missing 'BINARY_PATH' property.")
|
|
endif()
|
|
|
|
partition_table_add_check_size_target("${binary}_check_size"
|
|
DEPENDS "${binary_path}"
|
|
BINARY_PATH "${binary_path}"
|
|
PARTITION_TYPE app)
|
|
add_dependencies("${binary}" "${binary}_check_size")
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_check_binary_signed
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_check_binary_signed(<binary>)
|
|
|
|
*binary[in]*
|
|
|
|
Binary image target to which to add a partition size check. The
|
|
``binary`` target is created by the ``idf_build_binary``.
|
|
|
|
If the binary is not signed and signed applications are required with
|
|
CONFIG_SECURE_SIGNED_APPS, print a note indicating that the binary needs to
|
|
be signed manually. This situation can occur if the binary images are not
|
|
signed during the build because CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES is
|
|
not set.
|
|
#]]
|
|
function(idf_check_binary_signed binary)
|
|
if(NOT CONFIG_SECURE_SIGNED_APPS)
|
|
# Signed binaries are not required.
|
|
return()
|
|
endif()
|
|
|
|
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
|
|
# Binary should be already signed with idf_sign_binary.
|
|
return()
|
|
endif()
|
|
|
|
get_target_property(binary_path ${binary} BINARY_PATH)
|
|
if(NOT binary_path)
|
|
idf_die("Binary target '${binary}' is missing 'BINARY_PATH' property.")
|
|
endif()
|
|
|
|
if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
|
set(secure_boot_version "1")
|
|
elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME OR CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME)
|
|
set(secure_boot_version "2")
|
|
endif()
|
|
|
|
idf_component_get_property(espsecure_py_cmd esptool_py ESPSECUREPY_CMD)
|
|
string(REPLACE ";" " " espsecure_py_cmd "${espsecure_py_cmd}")
|
|
get_filename_component(binary_name "${binary_path}" NAME)
|
|
|
|
add_custom_command(TARGET "${binary}" POST_BUILD
|
|
COMMAND ${CMAKE_COMMAND} -E echo
|
|
"Binary '${binary_name}' not signed. Sign it before flashing with:"
|
|
COMMAND ${CMAKE_COMMAND} -E echo
|
|
"${espsecure_py_cmd} sign-data --keyfile KEYFILE --version ${secure_boot_version}"
|
|
"${binary_path}"
|
|
VERBATIM)
|
|
endfunction()
|
|
|
|
#[[
|
|
.. cmakev2:function:: idf_build_generate_depgraph
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_build_generate_depgraph(<executable>
|
|
[OUTPUT_FILE <file>])
|
|
|
|
*executable[in]*
|
|
|
|
Executable target for which to generate a dependency graph.
|
|
|
|
*OUTPUT_FILE[in,opt]*
|
|
|
|
Optional output file path for storing the dependency graph. If not
|
|
provided, the default path ``<build>/component_deps.dot`` is used.
|
|
|
|
Generate dependency graph for the specified ``executable`` and store it in
|
|
the specified ``OUTPUT_FILE``. If no ``OUTPUT_FILE`` is provided, the
|
|
default location ``<build>/component_deps.dot`` will be used.
|
|
#]]
|
|
function(idf_build_generate_depgraph executable)
|
|
set(options)
|
|
set(one_value OUTPUT_FILE)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
idf_build_get_property(depgraph_enabled __BUILD_COMPONENT_DEPGRAPH_ENABLED)
|
|
if(NOT depgraph_enabled)
|
|
return()
|
|
endif()
|
|
|
|
__get_executable_library_or_die(TARGET "${executable}" OUTPUT library)
|
|
|
|
idf_build_set_property(__BUILD_COMPONENT_DEPGRAPH "")
|
|
idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON)
|
|
|
|
idf_library_get_property(build_components "${library}" LIBRARY_COMPONENTS_LINKED)
|
|
|
|
foreach(component_name IN LISTS build_components)
|
|
idf_component_get_property(reqs "${component_name}" REQUIRES)
|
|
idf_component_get_property(component_format "${component_name}" COMPONENT_FORMAT)
|
|
if("${component_format}" STREQUAL "CMAKEV1")
|
|
# For cmakev1 components, also include commonly required
|
|
# components.
|
|
list(APPEND reqs ${common_reqs})
|
|
endif()
|
|
|
|
foreach(req IN LISTS reqs)
|
|
depgraph_add_edge(${component_name} ${req} REQUIRES)
|
|
endforeach()
|
|
|
|
idf_component_get_property(priv_reqs "${component_name}" PRIV_REQUIRES)
|
|
foreach(priv_req IN LISTS priv_reqs)
|
|
depgraph_add_edge(${component_name} ${priv_req} PRIV_REQUIRES)
|
|
endforeach()
|
|
endforeach()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT_FILE)
|
|
idf_build_get_property(build_dir BUILD_DIR)
|
|
set(ARG_OUTPUT_FILE "${build_dir}/component_deps.dot")
|
|
endif()
|
|
|
|
depgraph_generate("${ARG_OUTPUT_FILE}")
|
|
endfunction()
|