build.cmake 22 KB

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