1# idf_build_get_property 2# 3# @brief Retrieve the value of the specified property related to ESP-IDF build. 4# 5# @param[out] var the variable to store the value in 6# @param[in] property the property to get the value of 7# 8# @param[in, optional] GENERATOR_EXPRESSION (option) retrieve the generator expression for the property 9# instead of actual value 10function(idf_build_get_property var property) 11 cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN}) 12 if(__GENERATOR_EXPRESSION) 13 set(val "$<TARGET_PROPERTY:__idf_build_target,${property}>") 14 else() 15 get_property(val TARGET __idf_build_target PROPERTY ${property}) 16 endif() 17 set(${var} ${val} PARENT_SCOPE) 18endfunction() 19 20# idf_build_set_property 21# 22# @brief Set the value of the specified property related to ESP-IDF build. The property is 23# also added to the internal list of build properties if it isn't there already. 24# 25# @param[in] property the property to set the value of 26# @param[out] value value of the property 27# 28# @param[in, optional] APPEND (option) append the value to the current value of the 29# property instead of replacing it 30function(idf_build_set_property property value) 31 cmake_parse_arguments(_ "APPEND" "" "" ${ARGN}) 32 33 if(__APPEND) 34 set_property(TARGET __idf_build_target APPEND PROPERTY ${property} ${value}) 35 else() 36 set_property(TARGET __idf_build_target PROPERTY ${property} ${value}) 37 endif() 38 39 # Keep track of set build properties so that they can be exported to a file that 40 # will be included in early expansion script. 41 idf_build_get_property(build_properties __BUILD_PROPERTIES) 42 if(NOT property IN_LIST build_properties) 43 idf_build_set_property(__BUILD_PROPERTIES "${property}" APPEND) 44 endif() 45endfunction() 46 47# idf_build_unset_property 48# 49# @brief Unset the value of the specified property related to ESP-IDF build. Equivalent 50# to setting the property to an empty string; though it also removes the property 51# from the internal list of build properties. 52# 53# @param[in] property the property to unset the value of 54function(idf_build_unset_property property) 55 idf_build_set_property(${property} "") # set to an empty value 56 idf_build_get_property(build_properties __BUILD_PROPERTIES) # remove from tracked properties 57 list(REMOVE_ITEM build_properties ${property}) 58 idf_build_set_property(__BUILD_PROPERTIES "${build_properties}") 59endfunction() 60 61# 62# Retrieve the IDF_PATH repository's version, either using a version 63# file or Git revision. Sets the IDF_VER build property. 64# 65function(__build_get_idf_git_revision) 66 idf_build_get_property(idf_path IDF_PATH) 67 git_describe(idf_ver_git "${idf_path}") 68 if(EXISTS "${idf_path}/version.txt") 69 file(STRINGS "${idf_path}/version.txt" idf_ver_t) 70 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${idf_path}/version.txt") 71 else() 72 set(idf_ver_t ${idf_ver_git}) 73 endif() 74 # cut IDF_VER to required 32 characters. 75 string(SUBSTRING "${idf_ver_t}" 0 31 idf_ver) 76 idf_build_set_property(COMPILE_DEFINITIONS "-DIDF_VER=\"${idf_ver}\"" APPEND) 77 git_submodule_check("${idf_path}") 78 idf_build_set_property(IDF_VER ${idf_ver}) 79endfunction() 80 81# 82# Sets initial list of build specifications (compile options, definitions, etc.) common across 83# all library targets built under the ESP-IDF build system. These build specifications are added 84# privately using the directory-level CMake commands (add_compile_options, include_directories, etc.) 85# during component registration. 86# 87function(__build_set_default_build_specifications) 88 unset(compile_definitions) 89 unset(compile_options) 90 unset(c_compile_options) 91 unset(cxx_compile_options) 92 93 list(APPEND compile_definitions "-D_GNU_SOURCE") 94 95 list(APPEND compile_options "-ffunction-sections" 96 "-fdata-sections" 97 # warning-related flags 98 "-Wall" 99 "-Werror=all" 100 "-Wno-error=unused-function" 101 "-Wno-error=unused-variable" 102 "-Wno-error=deprecated-declarations" 103 "-Wextra" 104 "-Wno-unused-parameter" 105 "-Wno-sign-compare" 106 # always generate debug symbols (even in release mode, these don't 107 # go into the final binary so have no impact on size 108 "-ggdb") 109 110 list(APPEND c_compile_options "-std=gnu99" 111 "-Wno-old-style-declaration") 112 113 list(APPEND cxx_compile_options "-std=gnu++11") 114 115 idf_build_set_property(COMPILE_DEFINITIONS "${compile_definitions}" APPEND) 116 idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND) 117 idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND) 118 idf_build_set_property(CXX_COMPILE_OPTIONS "${cxx_compile_options}" APPEND) 119endfunction() 120 121# 122# Initialize the build. This gets called upon inclusion of idf.cmake to set internal 123# properties used for the processing phase of the build. 124# 125function(__build_init idf_path) 126 # Create the build target, to which the ESP-IDF build properties, dependencies are attached to. 127 # Must be global so as to be accessible from any subdirectory in custom projects. 128 add_library(__idf_build_target STATIC IMPORTED GLOBAL) 129 130 set_default(python "python") 131 132 idf_build_set_property(PYTHON ${python}) 133 idf_build_set_property(IDF_PATH ${idf_path}) 134 135 idf_build_set_property(__PREFIX idf) 136 idf_build_set_property(__CHECK_PYTHON 1) 137 138 __build_set_default_build_specifications() 139 140 # Add internal components to the build 141 idf_build_get_property(idf_path IDF_PATH) 142 idf_build_get_property(prefix __PREFIX) 143 file(GLOB component_dirs ${idf_path}/components/*) 144 foreach(component_dir ${component_dirs}) 145 get_filename_component(component_dir ${component_dir} ABSOLUTE) 146 __component_dir_quick_check(is_component ${component_dir}) 147 if(is_component) 148 __component_add(${component_dir} ${prefix}) 149 endif() 150 endforeach() 151 152 153 idf_build_get_property(target IDF_TARGET) 154 if(NOT target STREQUAL "linux") 155 # Set components required by all other components in the build 156 # 157 # - lwip is here so that #include <sys/socket.h> works without any special provisions 158 # - esp_hw_support is here for backward compatibility 159 set(requires_common cxx newlib freertos esp_hw_support heap log lwip soc hal esp_rom esp_common esp_system) 160 idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${requires_common}") 161 endif() 162 163 __build_get_idf_git_revision() 164 __kconfig_init() 165endfunction() 166 167# idf_build_component 168# 169# @brief Present a directory that contains a component to the build system. 170# Relative paths are converted to absolute paths with respect to current directory. 171# All calls to this command must be performed before idf_build_process. 172# 173# @note This command does not guarantee that the component will be processed 174# during build (see the COMPONENTS argument description for command idf_build_process) 175# 176# @param[in] component_dir directory of the component 177function(idf_build_component component_dir) 178 idf_build_get_property(prefix __PREFIX) 179 __component_add(${component_dir} ${prefix} 0) 180endfunction() 181 182# 183# Resolve the requirement component to the component target created for that component. 184# 185function(__build_resolve_and_add_req var component_target req type) 186 __component_get_target(_component_target ${req}) 187 __component_get_property(_component_registered ${component_target} __COMPONENT_REGISTERED) 188 if(NOT _component_target OR NOT _component_registered) 189 message(FATAL_ERROR "Failed to resolve component '${req}'.") 190 endif() 191 __component_set_property(${component_target} ${type} ${_component_target} APPEND) 192 set(${var} ${_component_target} PARENT_SCOPE) 193endfunction() 194 195# 196# Build a list of components (in the form of component targets) to be added to the build 197# based on public and private requirements. This list is saved in an internal property, 198# __BUILD_COMPONENT_TARGETS. 199# 200function(__build_expand_requirements component_target) 201 # Since there are circular dependencies, make sure that we do not infinitely 202 # expand requirements for each component. 203 idf_build_get_property(component_targets_seen __COMPONENT_TARGETS_SEEN) 204 __component_get_property(component_registered ${component_target} __COMPONENT_REGISTERED) 205 if(component_target IN_LIST component_targets_seen OR NOT component_registered) 206 return() 207 endif() 208 209 idf_build_set_property(__COMPONENT_TARGETS_SEEN ${component_target} APPEND) 210 211 get_property(reqs TARGET ${component_target} PROPERTY REQUIRES) 212 get_property(priv_reqs TARGET ${component_target} PROPERTY PRIV_REQUIRES) 213 214 foreach(req ${reqs}) 215 __build_resolve_and_add_req(_component_target ${component_target} ${req} __REQUIRES) 216 __build_expand_requirements(${_component_target}) 217 endforeach() 218 219 foreach(req ${priv_reqs}) 220 __build_resolve_and_add_req(_component_target ${component_target} ${req} __PRIV_REQUIRES) 221 __build_expand_requirements(${_component_target}) 222 endforeach() 223 224 idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS) 225 if(NOT component_target IN_LIST build_component_targets) 226 idf_build_set_property(__BUILD_COMPONENT_TARGETS ${component_target} APPEND) 227 228 __component_get_property(component_lib ${component_target} COMPONENT_LIB) 229 idf_build_set_property(__BUILD_COMPONENTS ${component_lib} APPEND) 230 231 idf_build_get_property(prefix __PREFIX) 232 __component_get_property(component_prefix ${component_target} __PREFIX) 233 234 __component_get_property(component_alias ${component_target} COMPONENT_ALIAS) 235 236 idf_build_set_property(BUILD_COMPONENT_ALIASES ${component_alias} APPEND) 237 238 # Only put in the prefix in the name if it is not the default one 239 if(component_prefix STREQUAL prefix) 240 __component_get_property(component_name ${component_target} COMPONENT_NAME) 241 idf_build_set_property(BUILD_COMPONENTS ${component_name} APPEND) 242 else() 243 idf_build_set_property(BUILD_COMPONENTS ${component_alias} APPEND) 244 endif() 245 endif() 246endfunction() 247 248# 249# Write a CMake file containing set build properties, owing to the fact that an internal 250# list of properties is maintained in idf_build_set_property() call. This is used to convert 251# those set properties to variables in the scope the output file is included in. 252# 253function(__build_write_properties output_file) 254 idf_build_get_property(build_properties __BUILD_PROPERTIES) 255 foreach(property ${build_properties}) 256 idf_build_get_property(val ${property}) 257 set(build_properties_text "${build_properties_text}\nset(${property} ${val})") 258 endforeach() 259 file(WRITE ${output_file} "${build_properties_text}") 260endfunction() 261 262# 263# Check if the Python interpreter used for the build has all the required modules. 264# 265function(__build_check_python) 266 idf_build_get_property(check __CHECK_PYTHON) 267 if(check) 268 idf_build_get_property(python PYTHON) 269 idf_build_get_property(idf_path IDF_PATH) 270 message(STATUS "Checking Python dependencies...") 271 execute_process(COMMAND "${python}" "${idf_path}/tools/check_python_dependencies.py" 272 RESULT_VARIABLE result) 273 if(NOT result EQUAL 0) 274 message(FATAL_ERROR "Some Python dependencies must be installed. Check above message for details.") 275 endif() 276 endif() 277endfunction() 278 279# 280# Prepare for component processing expanding each component's project include 281# 282macro(__build_process_project_includes) 283 # Include the sdkconfig cmake file, since the following operations require 284 # knowledge of config values. 285 idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE) 286 include(${sdkconfig_cmake}) 287 288 # Make each build property available as a read-only variable 289 idf_build_get_property(build_properties __BUILD_PROPERTIES) 290 foreach(build_property ${build_properties}) 291 idf_build_get_property(val ${build_property}) 292 set(${build_property} "${val}") 293 endforeach() 294 295 # Check that the CMake target value matches the Kconfig target value. 296 __target_check() 297 298 idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS) 299 300 # Include each component's project_include.cmake 301 foreach(component_target ${build_component_targets}) 302 __component_get_property(dir ${component_target} COMPONENT_DIR) 303 __component_get_property(_name ${component_target} COMPONENT_NAME) 304 set(COMPONENT_NAME ${_name}) 305 set(COMPONENT_DIR ${dir}) 306 set(COMPONENT_PATH ${dir}) # this is deprecated, users are encouraged to use COMPONENT_DIR; 307 # retained for compatibility 308 if(EXISTS ${COMPONENT_DIR}/project_include.cmake) 309 include(${COMPONENT_DIR}/project_include.cmake) 310 endif() 311 endforeach() 312endmacro() 313 314# 315# Utility macro for setting default property value if argument is not specified 316# for idf_build_process(). 317# 318macro(__build_set_default var default) 319 set(_var __${var}) 320 if(NOT "${${_var}}" STREQUAL "") 321 idf_build_set_property(${var} "${${_var}}") 322 else() 323 idf_build_set_property(${var} "${default}") 324 endif() 325 unset(_var) 326endmacro() 327 328# 329# Import configs as build instance properties so that they are accessible 330# using idf_build_get_config(). Config has to have been generated before calling 331# this command. 332# 333function(__build_import_configs) 334 # Include the sdkconfig cmake file, since the following operations require 335 # knowledge of config values. 336 idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE) 337 include(${sdkconfig_cmake}) 338 339 idf_build_set_property(__CONFIG_VARIABLES "${CONFIGS_LIST}") 340 foreach(config ${CONFIGS_LIST}) 341 set_property(TARGET __idf_build_target PROPERTY ${config} "${${config}}") 342 endforeach() 343endfunction() 344 345# idf_build_process 346# 347# @brief Main processing step for ESP-IDF build: config generation, adding components to the build, 348# dependency resolution, etc. 349# 350# @param[in] target ESP-IDF target 351# 352# @param[in, optional] PROJECT_DIR (single value) directory of the main project the buildsystem 353# is processed for; defaults to CMAKE_SOURCE_DIR 354# @param[in, optional] PROJECT_VER (single value) version string of the main project; defaults 355# to 1 356# @param[in, optional] PROJECT_NAME (single value) main project name, defaults to CMAKE_PROJECT_NAME 357# @param[in, optional] SDKCONFIG (single value) sdkconfig output path, defaults to PROJECT_DIR/sdkconfig 358# if PROJECT_DIR is set and CMAKE_SOURCE_DIR/sdkconfig if not 359# @param[in, optional] SDKCONFIG_DEFAULTS (single value) config defaults file to use for the build; defaults 360# to none (Kconfig defaults or previously generated config are used) 361# @param[in, optional] BUILD_DIR (single value) directory for build artifacts; defautls to CMAKE_BINARY_DIR 362# @param[in, optional] COMPONENTS (multivalue) select components to process among the components 363# known by the build system 364# (added via `idf_build_component`). This argument is used to trim the build. 365# Other components are automatically added if they are required 366# in the dependency chain, i.e. 367# the public and private requirements of the components in this list 368# are automatically added, and in 369# turn the public and private requirements of those requirements, 370# so on and so forth. If not specified, all components known to the build system 371# are processed. 372macro(idf_build_process target) 373 set(options) 374 set(single_value PROJECT_DIR PROJECT_VER PROJECT_NAME BUILD_DIR SDKCONFIG) 375 set(multi_value COMPONENTS SDKCONFIG_DEFAULTS) 376 cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" ${ARGN}) 377 378 idf_build_set_property(BOOTLOADER_BUILD "${BOOTLOADER_BUILD}") 379 380 # Check build target is specified. Since this target corresponds to a component 381 # name, the target component is automatically added to the list of common component 382 # requirements. 383 if(target STREQUAL "") 384 message(FATAL_ERROR "Build target not specified.") 385 endif() 386 387 idf_build_set_property(IDF_TARGET ${target}) 388 389 __build_set_default(PROJECT_DIR ${CMAKE_SOURCE_DIR}) 390 __build_set_default(PROJECT_NAME ${CMAKE_PROJECT_NAME}) 391 __build_set_default(PROJECT_VER 1) 392 __build_set_default(BUILD_DIR ${CMAKE_BINARY_DIR}) 393 394 idf_build_get_property(project_dir PROJECT_DIR) 395 __build_set_default(SDKCONFIG "${project_dir}/sdkconfig") 396 397 __build_set_default(SDKCONFIG_DEFAULTS "") 398 399 # Check for required Python modules 400 __build_check_python() 401 402 idf_build_get_property(target IDF_TARGET) 403 404 if(NOT target STREQUAL "linux") 405 idf_build_set_property(__COMPONENT_REQUIRES_COMMON ${target} APPEND) 406 else() 407 idf_build_set_property(__COMPONENT_REQUIRES_COMMON "") 408 endif() 409 410 # Perform early expansion of component CMakeLists.txt in CMake scripting mode. 411 # It is here we retrieve the public and private requirements of each component. 412 # It is also here we add the common component requirements to each component's 413 # own requirements. 414 __component_get_requirements() 415 416 idf_build_get_property(component_targets __COMPONENT_TARGETS) 417 418 # Finally, do component expansion. In this case it simply means getting a final list 419 # of build component targets given the requirements set by each component. 420 421 # Check if we need to trim the components first, and build initial components list 422 # from that. 423 if(__COMPONENTS) 424 unset(component_targets) 425 foreach(component ${__COMPONENTS}) 426 __component_get_target(component_target ${component}) 427 if(NOT component_target) 428 message(FATAL_ERROR "Failed to resolve component '${component}'.") 429 endif() 430 list(APPEND component_targets ${component_target}) 431 endforeach() 432 endif() 433 434 foreach(component_target ${component_targets}) 435 __build_expand_requirements(${component_target}) 436 endforeach() 437 idf_build_unset_property(__COMPONENT_TARGETS_SEEN) 438 439 # Get a list of common component requirements in component targets form (previously 440 # we just have a list of component names) 441 idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON) 442 foreach(common_req ${common_reqs}) 443 __component_get_target(component_target ${common_req}) 444 __component_get_property(lib ${component_target} COMPONENT_LIB) 445 idf_build_set_property(___COMPONENT_REQUIRES_COMMON ${lib} APPEND) 446 endforeach() 447 448 # Generate config values in different formats 449 idf_build_get_property(sdkconfig SDKCONFIG) 450 idf_build_get_property(sdkconfig_defaults SDKCONFIG_DEFAULTS) 451 __kconfig_generate_config("${sdkconfig}" "${sdkconfig_defaults}") 452 __build_import_configs() 453 454 # All targets built under this scope is with the ESP-IDF build system 455 set(ESP_PLATFORM 1) 456 idf_build_set_property(COMPILE_DEFINITIONS "-DESP_PLATFORM" APPEND) 457 458 # Perform component processing (inclusion of project_include.cmake, adding component 459 # subdirectories, creating library targets, linking libraries, etc.) 460 __build_process_project_includes() 461 462 idf_build_get_property(idf_path IDF_PATH) 463 add_subdirectory(${idf_path} ${build_dir}/esp-idf) 464 465 unset(ESP_PLATFORM) 466endmacro() 467 468# idf_build_executable 469# 470# @brief Specify the executable the build system can attach dependencies to (for generating 471# files used for linking, targets which should execute before creating the specified executable, 472# generating additional binary files, generating files related to flashing, etc.) 473function(idf_build_executable elf) 474 # Set additional link flags for the executable 475 idf_build_get_property(link_options LINK_OPTIONS) 476 # Using LINK_LIBRARIES here instead of LINK_OPTIONS, as the latter is not in CMake 3.5. 477 set_property(TARGET ${elf} APPEND PROPERTY LINK_LIBRARIES "${link_options}") 478 479 # Propagate link dependencies from component library targets to the executable 480 idf_build_get_property(link_depends __LINK_DEPENDS) 481 set_property(TARGET ${elf} APPEND PROPERTY LINK_DEPENDS "${link_depends}") 482 483 # Set the EXECUTABLE_NAME and EXECUTABLE properties since there are generator expression 484 # from components that depend on it 485 get_filename_component(elf_name ${elf} NAME_WE) 486 get_target_property(elf_dir ${elf} BINARY_DIR) 487 488 idf_build_set_property(EXECUTABLE_NAME ${elf_name}) 489 idf_build_set_property(EXECUTABLE ${elf}) 490 idf_build_set_property(EXECUTABLE_DIR "${elf_dir}") 491 492 # Add dependency of the build target to the executable 493 add_dependencies(${elf} __idf_build_target) 494endfunction() 495 496# idf_build_get_config 497# 498# @brief Get value of specified config variable 499function(idf_build_get_config var config) 500 cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN}) 501 if(__GENERATOR_EXPRESSION) 502 set(val "$<TARGET_PROPERTY:__idf_build_target,${config}>") 503 else() 504 get_property(val TARGET __idf_build_target PROPERTY ${config}) 505 endif() 506 set(${var} ${val} PARENT_SCOPE) 507endfunction() 508