project.cmake 34 KB

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