project.cmake 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. # Designed to be included from an IDF app's CMakeLists.txt file
  2. cmake_minimum_required(VERSION 3.16)
  3. include(${CMAKE_CURRENT_LIST_DIR}/targets.cmake)
  4. # Initialize build target for this build using the environment variable or
  5. # value passed externally.
  6. __target_init()
  7. # The mere inclusion of this CMake file sets up some interal build properties.
  8. # These properties can be modified in between this inclusion the the idf_build_process
  9. # call.
  10. include(${CMAKE_CURRENT_LIST_DIR}/idf.cmake)
  11. # setting PYTHON variable here for compatibility only, new code should use
  12. # idf_build_get_property(variable PYTHON)
  13. idf_build_get_property(PYTHON PYTHON)
  14. if(NOT PYTHON)
  15. message(FATAL_ERROR "Internal error, PYTHON build property not set correctly.")
  16. endif()
  17. # legacy variable for compatibility
  18. set(IDFTOOL ${PYTHON} "${IDF_PATH}/tools/idf.py")
  19. # On processing, checking Python required modules can be turned off if it was
  20. # already checked externally.
  21. if(PYTHON_DEPS_CHECKED)
  22. idf_build_set_property(__CHECK_PYTHON 0)
  23. endif()
  24. # Store CMake arguments that need to be passed into all CMake sub-projects as well
  25. # (bootloader, ULP, etc)
  26. #
  27. # It's not possible to tell if CMake was called with --warn-uninitialized, so to also
  28. # have these warnings in sub-projects we set a cache variable as well and then check that.
  29. if(WARN_UNINITIALIZED)
  30. idf_build_set_property(EXTRA_CMAKE_ARGS --warn-uninitialized)
  31. else()
  32. idf_build_set_property(EXTRA_CMAKE_ARGS "")
  33. endif()
  34. # Enable the component manager for regular projects if not explicitly disabled.
  35. if(NOT "$ENV{IDF_COMPONENT_MANAGER}" EQUAL "0")
  36. idf_build_set_property(IDF_COMPONENT_MANAGER 1)
  37. endif()
  38. # Set component manager interface version
  39. idf_build_set_property(__COMPONENT_MANAGER_INTERFACE_VERSION 2)
  40. #
  41. # Get the project version from either a version file or the Git revision. This is passed
  42. # to the idf_build_process call. Dependencies are also set here for when the version file
  43. # changes (if it is used).
  44. #
  45. function(__project_get_revision var)
  46. set(_project_path "${CMAKE_CURRENT_LIST_DIR}")
  47. if(NOT DEFINED PROJECT_VER)
  48. if(EXISTS "${_project_path}/version.txt")
  49. file(STRINGS "${_project_path}/version.txt" PROJECT_VER)
  50. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_project_path}/version.txt")
  51. else()
  52. git_describe(PROJECT_VER_GIT "${_project_path}")
  53. if(PROJECT_VER_GIT)
  54. set(PROJECT_VER ${PROJECT_VER_GIT})
  55. else()
  56. message(STATUS "Project is not inside a git repository, or git repository has no commits;"
  57. " will not use 'git describe' to determine PROJECT_VER.")
  58. set(PROJECT_VER 1)
  59. endif()
  60. endif()
  61. endif()
  62. set(${var} "${PROJECT_VER}" PARENT_SCOPE)
  63. endfunction()
  64. # paths_with_spaces_to_list
  65. #
  66. # Replacement for spaces2list in cases where it was previously used on
  67. # directory lists.
  68. #
  69. # If the variable doesn't contain spaces, (e.g. is already a CMake list)
  70. # then the variable is unchanged. Otherwise an external Python script is called
  71. # to try to split the paths, and the variable is updated with the result.
  72. #
  73. # This feature is added only for compatibility. Please do not introduce new
  74. # space separated path lists.
  75. #
  76. function(paths_with_spaces_to_list variable_name)
  77. if("${${variable_name}}" MATCHES "[ \t]")
  78. idf_build_get_property(python PYTHON)
  79. idf_build_get_property(idf_path IDF_PATH)
  80. execute_process(
  81. COMMAND ${python}
  82. "${idf_path}/tools/split_paths_by_spaces.py"
  83. "--var-name=${variable_name}"
  84. "${${variable_name}}"
  85. WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}"
  86. OUTPUT_VARIABLE result
  87. RESULT_VARIABLE ret)
  88. if(NOT ret EQUAL 0)
  89. message(FATAL_ERROR "Failed to parse ${variable_name}, see diagnostics above")
  90. endif()
  91. set("${variable_name}" "${result}" PARENT_SCOPE)
  92. endif()
  93. endfunction()
  94. #
  95. # Output the built components to the user. Generates files for invoking esp_idf_monitor
  96. # that doubles as an overview of some of the more important build properties.
  97. #
  98. function(__project_info test_components)
  99. idf_build_get_property(prefix __PREFIX)
  100. idf_build_get_property(_build_components BUILD_COMPONENTS)
  101. idf_build_get_property(build_dir BUILD_DIR)
  102. idf_build_get_property(idf_path IDF_PATH)
  103. list(SORT _build_components)
  104. unset(build_components)
  105. unset(build_component_paths)
  106. foreach(build_component ${_build_components})
  107. __component_get_target(component_target "${build_component}")
  108. __component_get_property(_name ${component_target} COMPONENT_NAME)
  109. __component_get_property(_prefix ${component_target} __PREFIX)
  110. __component_get_property(_alias ${component_target} COMPONENT_ALIAS)
  111. __component_get_property(_dir ${component_target} COMPONENT_DIR)
  112. if(_alias IN_LIST test_components)
  113. list(APPEND test_component_paths ${_dir})
  114. else()
  115. if(_prefix STREQUAL prefix)
  116. set(component ${_name})
  117. else()
  118. set(component ${_alias})
  119. endif()
  120. list(APPEND build_components ${component})
  121. list(APPEND build_component_paths ${_dir})
  122. endif()
  123. endforeach()
  124. set(PROJECT_NAME ${CMAKE_PROJECT_NAME})
  125. idf_build_get_property(PROJECT_PATH PROJECT_DIR)
  126. idf_build_get_property(BUILD_DIR BUILD_DIR)
  127. idf_build_get_property(SDKCONFIG SDKCONFIG)
  128. idf_build_get_property(SDKCONFIG_DEFAULTS SDKCONFIG_DEFAULTS)
  129. idf_build_get_property(PROJECT_EXECUTABLE EXECUTABLE)
  130. set(PROJECT_BIN ${CMAKE_PROJECT_NAME}.bin)
  131. idf_build_get_property(IDF_VER IDF_VER)
  132. idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE)
  133. include(${sdkconfig_cmake})
  134. idf_build_get_property(COMPONENT_KCONFIGS KCONFIGS)
  135. idf_build_get_property(COMPONENT_KCONFIGS_PROJBUILD KCONFIG_PROJBUILDS)
  136. idf_build_get_property(debug_prefix_map_gdbinit DEBUG_PREFIX_MAP_GDBINIT)
  137. if(CONFIG_APP_BUILD_TYPE_RAM)
  138. set(PROJECT_BUILD_TYPE ram_app)
  139. else()
  140. set(PROJECT_BUILD_TYPE flash_app)
  141. endif()
  142. # Write project description JSON file
  143. idf_build_get_property(build_dir BUILD_DIR)
  144. make_json_list("${build_components};${test_components}" build_components_json)
  145. make_json_list("${build_component_paths};${test_component_paths}" build_component_paths_json)
  146. configure_file("${idf_path}/tools/cmake/project_description.json.in"
  147. "${build_dir}/project_description.json")
  148. # Generate component dependency graph
  149. depgraph_generate("${build_dir}/component_deps.dot")
  150. # We now have the following component-related variables:
  151. #
  152. # build_components is the list of components to include in the build.
  153. # build_component_paths is the paths to all of these components, obtained from the component dependencies file.
  154. #
  155. # Print the list of found components and test components
  156. string(REPLACE ";" " " build_components "${build_components}")
  157. string(REPLACE ";" " " build_component_paths "${build_component_paths}")
  158. message(STATUS "Components: ${build_components}")
  159. message(STATUS "Component paths: ${build_component_paths}")
  160. if(test_components)
  161. string(REPLACE ";" " " test_components "${test_components}")
  162. string(REPLACE ";" " " test_component_paths "${test_component_paths}")
  163. message(STATUS "Test components: ${test_components}")
  164. message(STATUS "Test component paths: ${test_component_paths}")
  165. endif()
  166. endfunction()
  167. function(__project_init components_var test_components_var)
  168. # Use EXTRA_CFLAGS, EXTRA_CXXFLAGS and EXTRA_CPPFLAGS to add more priority options to the compiler
  169. # EXTRA_CPPFLAGS is used for both C and C++
  170. # Unlike environments' CFLAGS/CXXFLAGS/CPPFLAGS which work for both host and target build,
  171. # these works only for target build
  172. set(extra_cflags "$ENV{EXTRA_CFLAGS}")
  173. set(extra_cxxflags "$ENV{EXTRA_CXXFLAGS}")
  174. set(extra_cppflags "$ENV{EXTRA_CPPFLAGS}")
  175. spaces2list(extra_cflags)
  176. spaces2list(extra_cxxflags)
  177. spaces2list(extra_cppflags)
  178. idf_build_set_property(C_COMPILE_OPTIONS "${extra_cflags}" APPEND)
  179. idf_build_set_property(CXX_COMPILE_OPTIONS "${extra_cxxflags}" APPEND)
  180. idf_build_set_property(COMPILE_OPTIONS "${extra_cppflags}" APPEND)
  181. function(__project_component_dir component_dir)
  182. get_filename_component(component_dir "${component_dir}" ABSOLUTE)
  183. # The directory itself is a valid idf component
  184. if(EXISTS ${component_dir}/CMakeLists.txt)
  185. idf_build_component(${component_dir})
  186. else()
  187. # otherwise, check whether the subfolders are potential idf components
  188. file(GLOB component_dirs ${component_dir}/*)
  189. foreach(component_dir ${component_dirs})
  190. if(IS_DIRECTORY ${component_dir})
  191. __component_dir_quick_check(is_component ${component_dir})
  192. if(is_component)
  193. idf_build_component(${component_dir})
  194. endif()
  195. endif()
  196. endforeach()
  197. endif()
  198. endfunction()
  199. # Add component directories to the build, given the component filters, exclusions
  200. # extra directories, etc. passed from the root CMakeLists.txt.
  201. if(COMPONENT_DIRS)
  202. # User wants to fully override where components are pulled from.
  203. paths_with_spaces_to_list(COMPONENT_DIRS)
  204. idf_build_set_property(__COMPONENT_TARGETS "")
  205. foreach(component_dir ${COMPONENT_DIRS})
  206. get_filename_component(component_abs_path ${component_dir} ABSOLUTE)
  207. if(NOT EXISTS ${component_abs_path})
  208. message(FATAL_ERROR "Directory specified in COMPONENT_DIRS doesn't exist: ${component_abs_path}")
  209. endif()
  210. __project_component_dir(${component_dir})
  211. endforeach()
  212. else()
  213. if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/main")
  214. __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/main")
  215. endif()
  216. paths_with_spaces_to_list(EXTRA_COMPONENT_DIRS)
  217. foreach(component_dir ${EXTRA_COMPONENT_DIRS})
  218. get_filename_component(component_abs_path ${component_dir} ABSOLUTE)
  219. if(NOT EXISTS ${component_abs_path})
  220. message(FATAL_ERROR "Directory specified in EXTRA_COMPONENT_DIRS doesn't exist: ${component_abs_path}")
  221. endif()
  222. __project_component_dir("${component_dir}")
  223. endforeach()
  224. # Look for components in the usual places: CMAKE_CURRENT_LIST_DIR/main,
  225. # extra component dirs, and CMAKE_CURRENT_LIST_DIR/components
  226. __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/components")
  227. endif()
  228. # For bootloader components, we only need to set-up the Kconfig files.
  229. # Indeed, bootloader is currently compiled as a subproject, thus,
  230. # its components are not part of the main project.
  231. # However, in order to be able to configure these bootloader components
  232. # using menuconfig, we need to look for their Kconfig-related files now.
  233. file(GLOB bootloader_component_dirs "${CMAKE_CURRENT_LIST_DIR}/bootloader_components/*")
  234. list(SORT bootloader_component_dirs)
  235. foreach(bootloader_component_dir ${bootloader_component_dirs})
  236. if(IS_DIRECTORY ${bootloader_component_dir})
  237. __component_dir_quick_check(is_component ${bootloader_component_dir})
  238. if(is_component)
  239. __kconfig_bootloader_component_add("${bootloader_component_dir}")
  240. endif()
  241. endif()
  242. endforeach()
  243. spaces2list(COMPONENTS)
  244. spaces2list(EXCLUDE_COMPONENTS)
  245. idf_build_get_property(component_targets __COMPONENT_TARGETS)
  246. foreach(component_target ${component_targets})
  247. __component_get_property(component_name ${component_target} COMPONENT_NAME)
  248. set(include 1)
  249. if(COMPONENTS AND NOT component_name IN_LIST COMPONENTS)
  250. set(include 0)
  251. endif()
  252. if(EXCLUDE_COMPONENTS AND component_name IN_LIST EXCLUDE_COMPONENTS)
  253. set(include 0)
  254. endif()
  255. if(include)
  256. list(APPEND components ${component_name})
  257. endif()
  258. endforeach()
  259. if(TESTS_ALL OR BUILD_TESTS OR TEST_COMPONENTS OR TEST_EXCLUDE_COMPONENTS)
  260. spaces2list(TEST_COMPONENTS)
  261. spaces2list(TEST_EXCLUDE_COMPONENTS)
  262. idf_build_get_property(component_targets __COMPONENT_TARGETS)
  263. foreach(component_target ${component_targets})
  264. __component_get_property(component_dir ${component_target} COMPONENT_DIR)
  265. __component_get_property(component_name ${component_target} COMPONENT_NAME)
  266. if(component_name IN_LIST components)
  267. set(include 1)
  268. if(TEST_COMPONENTS AND NOT component_name IN_LIST TEST_COMPONENTS)
  269. set(include 0)
  270. endif()
  271. if(TEST_EXCLUDE_COMPONENTS AND component_name IN_LIST TEST_EXCLUDE_COMPONENTS)
  272. set(include 0)
  273. endif()
  274. if(include AND EXISTS ${component_dir}/test)
  275. __component_add(${component_dir}/test ${component_name})
  276. list(APPEND test_components ${component_name}::test)
  277. endif()
  278. endif()
  279. endforeach()
  280. endif()
  281. set(${components_var} "${components}" PARENT_SCOPE)
  282. set(${test_components_var} "${test_components}" PARENT_SCOPE)
  283. endfunction()
  284. # Trick to temporarily redefine project(). When functions are overriden in CMake, the originals can still be accessed
  285. # using an underscore prefixed function of the same name. The following lines make sure that __project calls
  286. # the original project(). See https://cmake.org/pipermail/cmake/2015-October/061751.html.
  287. function(project)
  288. endfunction()
  289. function(_project)
  290. endfunction()
  291. macro(project project_name)
  292. # Initialize project, preparing COMPONENTS argument for idf_build_process()
  293. # call later using external COMPONENT_DIRS, COMPONENTS_DIRS, EXTRA_COMPONENTS_DIR,
  294. # EXTRA_COMPONENTS_DIRS, COMPONENTS, EXLUDE_COMPONENTS, TEST_COMPONENTS,
  295. # TEST_EXLUDE_COMPONENTS, TESTS_ALL, BUILD_TESTS
  296. __project_init(components test_components)
  297. __target_set_toolchain()
  298. if(CCACHE_ENABLE)
  299. find_program(CCACHE_FOUND ccache)
  300. if(CCACHE_FOUND)
  301. message(STATUS "ccache will be used for faster recompilation")
  302. set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
  303. else()
  304. message(WARNING "enabled ccache in build but ccache program not found")
  305. endif()
  306. endif()
  307. # The actual call to project()
  308. __project(${project_name} C CXX ASM)
  309. # Generate compile_commands.json (needs to come after project call).
  310. set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
  311. # If CMAKE_COLOR_DIAGNOSTICS not set in project CMakeLists.txt or in the environment,
  312. # enable it by default.
  313. if(NOT DEFINED CMAKE_COLOR_DIAGNOSTICS AND NOT DEFINED ENV{CMAKE_COLOR_DIAGNOSTICS})
  314. set(CMAKE_COLOR_DIAGNOSTICS ON)
  315. endif()
  316. # Since components can import third-party libraries, the original definition of project() should be restored
  317. # before the call to add components to the build.
  318. function(project)
  319. set(project_ARGV ARGV)
  320. __project(${${project_ARGV}})
  321. # Set the variables that project() normally sets, documented in the
  322. # command's docs.
  323. #
  324. # https://cmake.org/cmake/help/v3.16/command/project.html
  325. #
  326. # There is some nuance when it comes to setting version variables in terms of whether
  327. # CMP0048 is set to OLD or NEW. However, the proper behavior should have bee already handled by the original
  328. # project call, and we're just echoing the values those variables were set to.
  329. set(PROJECT_NAME "${PROJECT_NAME}" PARENT_SCOPE)
  330. set(PROJECT_BINARY_DIR "${PROJECT_BINARY_DIR}" PARENT_SCOPE)
  331. set(PROJECT_SOURCE_DIR "${PROJECT_SOURCE_DIR}" PARENT_SCOPE)
  332. set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE)
  333. set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE)
  334. set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE)
  335. set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE)
  336. set(PROJECT_VERSION_TWEAK "${PROJECT_VERSION_TWEAK}" PARENT_SCOPE)
  337. set(PROJECT_DESCRIPTION "${PROJECT_DESCRIPTION}" PARENT_SCOPE)
  338. set(PROJECT_HOMEPAGE_URL "${PROJECT_HOMEPAGE_URL}" PARENT_SCOPE)
  339. set(${PROJECT_NAME}_BINARY_DIR "${${PROJECT_NAME}_BINARY_DIR}" PARENT_SCOPE)
  340. set(${PROJECT_NAME}_SOURCE_DIR "${${PROJECT_NAME}_SOURCE_DIR}" PARENT_SCOPE)
  341. set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}" PARENT_SCOPE)
  342. set(${PROJECT_NAME}_VERSION_MAJOR "${${PROJECT_NAME}_VERSION_MAJOR}" PARENT_SCOPE)
  343. set(${PROJECT_NAME}_VERSION_MINOR "${${PROJECT_NAME}_VERSION_MINOR}" PARENT_SCOPE)
  344. set(${PROJECT_NAME}_VERSION_PATCH "${${PROJECT_NAME}_VERSION_PATCH}" PARENT_SCOPE)
  345. set(${PROJECT_NAME}_VERSION_TWEAK "${${PROJECT_NAME}_VERSION_TWEAK}" PARENT_SCOPE)
  346. set(${PROJECT_NAME}_DESCRIPTION "${${PROJECT_NAME}_DESCRIPTION}" PARENT_SCOPE)
  347. set(${PROJECT_NAME}_HOMEPAGE_URL "${${PROJECT_NAME}_HOMEPAGE_URL}" PARENT_SCOPE)
  348. endfunction()
  349. # Prepare the following arguments for the idf_build_process() call using external
  350. # user values:
  351. #
  352. # SDKCONFIG_DEFAULTS is from external SDKCONFIG_DEFAULTS
  353. # SDKCONFIG is from external SDKCONFIG
  354. # BUILD_DIR is set to project binary dir
  355. #
  356. # PROJECT_NAME is taken from the passed name from project() call
  357. # PROJECT_DIR is set to the current directory
  358. # PROJECT_VER is from the version text or git revision of the current repo
  359. # SDKCONFIG_DEFAULTS environment variable may specify a file name relative to the root of the project.
  360. # When building the bootloader, ignore this variable, since:
  361. # 1. The bootloader project uses an existing SDKCONFIG file from the top-level project
  362. # 2. File specified by SDKCONFIG_DEFAULTS will not be found relative to the root of the bootloader project
  363. if(NOT BOOTLOADER_BUILD)
  364. set(_sdkconfig_defaults "$ENV{SDKCONFIG_DEFAULTS}")
  365. endif()
  366. if(NOT _sdkconfig_defaults)
  367. if(EXISTS "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
  368. set(_sdkconfig_defaults "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
  369. else()
  370. set(_sdkconfig_defaults "")
  371. endif()
  372. endif()
  373. if(SDKCONFIG_DEFAULTS)
  374. set(_sdkconfig_defaults "${SDKCONFIG_DEFAULTS}")
  375. endif()
  376. foreach(sdkconfig_default ${_sdkconfig_defaults})
  377. get_filename_component(sdkconfig_default "${sdkconfig_default}" ABSOLUTE)
  378. if(NOT EXISTS "${sdkconfig_default}")
  379. message(FATAL_ERROR "SDKCONFIG_DEFAULTS '${sdkconfig_default}' does not exist.")
  380. endif()
  381. list(APPEND sdkconfig_defaults ${sdkconfig_default})
  382. endforeach()
  383. if(SDKCONFIG)
  384. get_filename_component(sdkconfig "${SDKCONFIG}" ABSOLUTE)
  385. else()
  386. set(sdkconfig "${CMAKE_CURRENT_LIST_DIR}/sdkconfig")
  387. endif()
  388. if(BUILD_DIR)
  389. get_filename_component(build_dir "${BUILD_DIR}" ABSOLUTE)
  390. if(NOT EXISTS "${build_dir}")
  391. message(FATAL_ERROR "BUILD_DIR '${build_dir}' does not exist.")
  392. endif()
  393. else()
  394. set(build_dir ${CMAKE_BINARY_DIR})
  395. endif()
  396. __project_get_revision(project_ver)
  397. message(STATUS "Building ESP-IDF components for target ${IDF_TARGET}")
  398. idf_build_process(${IDF_TARGET}
  399. SDKCONFIG_DEFAULTS "${sdkconfig_defaults}"
  400. SDKCONFIG ${sdkconfig}
  401. BUILD_DIR ${build_dir}
  402. PROJECT_NAME ${CMAKE_PROJECT_NAME}
  403. PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR}
  404. PROJECT_VER "${project_ver}"
  405. COMPONENTS "${components};${test_components}")
  406. # Special treatment for 'main' component for standard projects (not part of core build system).
  407. # Have it depend on every other component in the build. This is
  408. # a convenience behavior for the standard project; thus is done outside of the core build system
  409. # so that it treats components equally.
  410. #
  411. # This behavior should only be when user did not set REQUIRES/PRIV_REQUIRES manually.
  412. idf_build_get_property(build_components BUILD_COMPONENT_ALIASES)
  413. if(idf::main IN_LIST build_components)
  414. __component_get_target(main_target idf::main)
  415. __component_get_property(reqs ${main_target} REQUIRES)
  416. __component_get_property(priv_reqs ${main_target} PRIV_REQUIRES)
  417. __component_get_property(managed_reqs ${main_target} MANAGED_REQUIRES)
  418. __component_get_property(managed_priv_reqs ${main_target} MANAGED_PRIV_REQUIRES)
  419. #if user has not set any requirements, except ones added with the component manager
  420. if((NOT reqs OR reqs STREQUAL managed_reqs) AND (NOT priv_reqs OR priv_reqs STREQUAL managed_priv_reqs))
  421. if(test_components)
  422. list(REMOVE_ITEM build_components ${test_components})
  423. endif()
  424. list(REMOVE_ITEM build_components idf::main)
  425. __component_get_property(lib ${main_target} COMPONENT_LIB)
  426. set_property(TARGET ${lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${build_components}")
  427. get_property(type TARGET ${lib} PROPERTY TYPE)
  428. if(type STREQUAL STATIC_LIBRARY)
  429. set_property(TARGET ${lib} APPEND PROPERTY LINK_LIBRARIES "${build_components}")
  430. endif()
  431. endif()
  432. endif()
  433. set(project_elf ${CMAKE_PROJECT_NAME}.elf)
  434. # Create a dummy file to work around CMake requirement of having a source file while adding an
  435. # executable. This is also used by idf_size.py to detect the target
  436. set(project_elf_src ${CMAKE_BINARY_DIR}/project_elf_src_${IDF_TARGET}.c)
  437. add_custom_command(OUTPUT ${project_elf_src}
  438. COMMAND ${CMAKE_COMMAND} -E touch ${project_elf_src}
  439. VERBATIM)
  440. add_custom_target(_project_elf_src DEPENDS "${project_elf_src}")
  441. add_executable(${project_elf} "${project_elf_src}")
  442. add_dependencies(${project_elf} _project_elf_src)
  443. if(__PROJECT_GROUP_LINK_COMPONENTS)
  444. target_link_libraries(${project_elf} PRIVATE "-Wl,--start-group")
  445. endif()
  446. if(test_components)
  447. target_link_libraries(${project_elf} PRIVATE "-Wl,--whole-archive")
  448. foreach(test_component ${test_components})
  449. if(TARGET ${test_component})
  450. target_link_libraries(${project_elf} PRIVATE ${test_component})
  451. endif()
  452. endforeach()
  453. target_link_libraries(${project_elf} PRIVATE "-Wl,--no-whole-archive")
  454. endif()
  455. idf_build_get_property(build_components BUILD_COMPONENT_ALIASES)
  456. if(test_components)
  457. list(REMOVE_ITEM build_components ${test_components})
  458. endif()
  459. if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
  460. # Compiling for the host, and the host is macOS, so the linker is Darwin LD.
  461. # Note, when adding support for Clang and LLD based toolchain this check will
  462. # need to be modified.
  463. set(linker_type "Darwin")
  464. else()
  465. set(linker_type "GNU")
  466. endif()
  467. foreach(build_component ${build_components})
  468. __component_get_target(build_component_target ${build_component})
  469. __component_get_property(whole_archive ${build_component_target} WHOLE_ARCHIVE)
  470. if(whole_archive)
  471. if(linker_type STREQUAL "GNU")
  472. message(STATUS "Component ${build_component} will be linked with -Wl,--whole-archive")
  473. target_link_libraries(${project_elf} PRIVATE
  474. "-Wl,--whole-archive"
  475. ${build_component}
  476. "-Wl,--no-whole-archive")
  477. elseif(linker_type STREQUAL "Darwin")
  478. message(STATUS "Component ${build_component} will be linked with -Wl,-force_load")
  479. target_link_libraries(${project_elf} PRIVATE
  480. "-Wl,-force_load"
  481. ${build_component})
  482. endif()
  483. else()
  484. target_link_libraries(${project_elf} PRIVATE ${build_component})
  485. endif()
  486. endforeach()
  487. if(linker_type STREQUAL "GNU")
  488. set(mapfile "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.map")
  489. set(idf_target "${IDF_TARGET}")
  490. string(TOUPPER ${idf_target} idf_target)
  491. # Add cross-reference table to the map file
  492. target_link_options(${project_elf} PRIVATE "-Wl,--cref")
  493. # Add this symbol as a hint for idf_size.py to guess the target name
  494. target_link_options(${project_elf} PRIVATE "-Wl,--defsym=IDF_TARGET_${idf_target}=0")
  495. # Enable map file output
  496. target_link_options(${project_elf} PRIVATE "-Wl,--Map=${mapfile}")
  497. # Check if linker supports --no-warn-rwx-segments
  498. execute_process(COMMAND ${CMAKE_LINKER} "--no-warn-rwx-segments" "--version"
  499. RESULT_VARIABLE result
  500. OUTPUT_QUIET
  501. ERROR_QUIET)
  502. if(${result} EQUAL 0)
  503. # Do not print RWX segment warnings
  504. target_link_options(${project_elf} PRIVATE "-Wl,--no-warn-rwx-segments")
  505. endif()
  506. unset(idf_target)
  507. endif()
  508. set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY
  509. ADDITIONAL_MAKE_CLEAN_FILES
  510. "${mapfile}" "${project_elf_src}")
  511. idf_build_get_property(idf_path IDF_PATH)
  512. idf_build_get_property(python PYTHON)
  513. set(idf_size ${python} ${idf_path}/tools/idf_size.py)
  514. # Add size targets, depend on map file, run idf_size.py
  515. # OUTPUT_JSON is passed for compatibility reasons, SIZE_OUTPUT_FORMAT
  516. # environment variable is recommended and has higher priority
  517. add_custom_target(size
  518. COMMAND ${CMAKE_COMMAND}
  519. -D "IDF_SIZE_TOOL=${idf_size}"
  520. -D "MAP_FILE=${mapfile}"
  521. -D "OUTPUT_JSON=${OUTPUT_JSON}"
  522. -P "${idf_path}/tools/cmake/run_size_tool.cmake"
  523. DEPENDS ${mapfile}
  524. USES_TERMINAL
  525. VERBATIM
  526. )
  527. add_custom_target(size-files
  528. COMMAND ${CMAKE_COMMAND}
  529. -D "IDF_SIZE_TOOL=${idf_size}"
  530. -D "IDF_SIZE_MODE=--files"
  531. -D "MAP_FILE=${mapfile}"
  532. -D "OUTPUT_JSON=${OUTPUT_JSON}"
  533. -P "${idf_path}/tools/cmake/run_size_tool.cmake"
  534. DEPENDS ${mapfile}
  535. USES_TERMINAL
  536. VERBATIM
  537. )
  538. add_custom_target(size-components
  539. COMMAND ${CMAKE_COMMAND}
  540. -D "IDF_SIZE_TOOL=${idf_size}"
  541. -D "IDF_SIZE_MODE=--archives"
  542. -D "MAP_FILE=${mapfile}"
  543. -D "OUTPUT_JSON=${OUTPUT_JSON}"
  544. -P "${idf_path}/tools/cmake/run_size_tool.cmake"
  545. DEPENDS ${mapfile}
  546. USES_TERMINAL
  547. VERBATIM
  548. )
  549. unset(idf_size)
  550. # Add DFU build and flash targets
  551. __add_dfu_targets()
  552. # Add UF2 build targets
  553. __add_uf2_targets()
  554. idf_build_executable(${project_elf})
  555. __project_info("${test_components}")
  556. endmacro()