idf_functions.cmake 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #
  2. # Load cmake modules
  3. #
  4. get_property(__idf_environment_set GLOBAL PROPERTY __IDF_ENVIRONMENT_SET)
  5. if(NOT __idf_environment_set)
  6. # Set IDF_PATH, as nothing else will work without this.
  7. set(IDF_PATH "$ENV{IDF_PATH}")
  8. if(NOT IDF_PATH)
  9. # Documentation says you should set IDF_PATH in your environment, but we
  10. # can infer it relative to tools/cmake directory if it's not set.
  11. get_filename_component(IDF_PATH "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
  12. endif()
  13. file(TO_CMAKE_PATH "${IDF_PATH}" IDF_PATH)
  14. set(ENV{IDF_PATH} ${IDF_PATH})
  15. set(CMAKE_MODULE_PATH
  16. "${IDF_PATH}/tools/cmake"
  17. "${IDF_PATH}/tools/cmake/third_party"
  18. ${CMAKE_MODULE_PATH})
  19. include(utilities)
  20. include(components)
  21. include(kconfig)
  22. include(targets)
  23. include(git_submodules)
  24. include(GetGitRevisionDescription)
  25. include(crosstool_version_check)
  26. include(ldgen)
  27. include(version)
  28. set_default(PYTHON "python")
  29. if(NOT PYTHON_DEPS_CHECKED AND NOT BOOTLOADER_BUILD)
  30. message(STATUS "Checking Python dependencies...")
  31. execute_process(COMMAND "${PYTHON}" "${IDF_PATH}/tools/check_python_dependencies.py"
  32. RESULT_VARIABLE result)
  33. if(NOT result EQUAL 0)
  34. message(FATAL_ERROR "Some Python dependencies must be installed. Check above message for details.")
  35. endif()
  36. endif()
  37. idf_set_target()
  38. set_property(GLOBAL APPEND PROPERTY __IDF_COMPONENTS_PREFIX "idf_component")
  39. set_property(GLOBAL PROPERTY __IDF_ENVIRONMENT_SET 1)
  40. endif()
  41. macro(idf_set_variables)
  42. set_default(IDF_BUILD_ARTIFACTS OFF)
  43. if(IDF_BUILD_ARTIFACTS)
  44. if(NOT IDF_BUILD_ARTIFACTS_DIR OR NOT IDF_PROJECT_EXECUTABLE)
  45. message(FATAL_ERROR "IDF_BUILD_ARTIFACTS and IDF_PROJECT_EXECUTABLE needs to be specified \
  46. if IDF_BUILD_ARTIFACTS is ON.")
  47. endif()
  48. endif()
  49. set_default(IDF_COMPONENT_DIRS "${IDF_EXTRA_COMPONENT_DIRS} ${IDF_PATH}/components")
  50. set_default(IDF_COMPONENTS "")
  51. set_default(IDF_COMPONENT_REQUIRES_COMMON "cxx ${IDF_TARGET} newlib freertos heap log soc")
  52. set(IDF_PROJECT_PATH "${CMAKE_SOURCE_DIR}")
  53. set(ESP_PLATFORM 1 CACHE BOOL INTERNAL)
  54. spaces2list(IDF_COMPONENT_DIRS)
  55. spaces2list(IDF_COMPONENTS)
  56. spaces2list(IDF_COMPONENT_REQUIRES_COMMON)
  57. endmacro()
  58. # Add all the IDF global compiler & preprocessor options
  59. # (applied to all components). Some are config-dependent
  60. #
  61. # If you only want to set options for a particular component,
  62. # don't call or edit this function. TODO DESCRIBE WHAT TO DO INSTEAD
  63. #
  64. function(idf_set_global_compile_options)
  65. # Temporary trick to support both gcc5 and gcc8 builds
  66. if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0)
  67. set(GCC_NOT_5_2_0 0 CACHE STRING "GCC is 5.2.0 version")
  68. else()
  69. set(GCC_NOT_5_2_0 1 CACHE STRING "GCC is not 5.2.0 version")
  70. endif()
  71. list(APPEND compile_definitions "GCC_NOT_5_2_0=${GCC_NOT_5_2_0}")
  72. list(APPEND compile_definitions "ESP_PLATFORM" "HAVE_CONFIG_H")
  73. list(APPEND compile_options "${CMAKE_C_FLAGS}")
  74. list(APPEND c_compile_options "${CMAKE_C_FLAGS}")
  75. list(APPEND cxx_compile_options "${CMAKE_CXX_FLAGS}")
  76. if(CONFIG_OPTIMIZATION_LEVEL_RELEASE)
  77. list(APPEND compile_options "-Os")
  78. else()
  79. list(APPEND compile_options "-Og")
  80. endif()
  81. list(APPEND c_compile_options "-std=gnu99")
  82. list(APPEND cxx_compile_options "-std=gnu++11" "-fno-rtti")
  83. if(CONFIG_CXX_EXCEPTIONS)
  84. list(APPEND cxx_compile_options "-fexceptions")
  85. else()
  86. list(APPEND cxx_compile_options "-fno-exceptions")
  87. endif()
  88. # Default compiler configuration
  89. list(APPEND compile_options "-ffunction-sections"
  90. "-fdata-sections"
  91. "-fstrict-volatile-bitfields"
  92. "-nostdlib")
  93. list(APPEND compile_options "-Wall"
  94. "-Werror=all"
  95. "-Wno-error=unused-function"
  96. "-Wno-error=unused-but-set-variable"
  97. "-Wno-error=unused-variable"
  98. "-Wno-error=deprecated-declarations"
  99. "-Wextra"
  100. "-Wno-unused-parameter"
  101. "-Wno-sign-compare")
  102. list(APPEND c_compile_options "-Wno-old-style-declaration")
  103. if(CONFIG_DISABLE_GCC8_WARNINGS)
  104. list(APPEND compile_options
  105. "-Wno-parentheses"
  106. "-Wno-sizeof-pointer-memaccess"
  107. "-Wno-clobbered"
  108. )
  109. # doesn't use GCC_NOT_5_2_0 because idf_set_global_variables was not called before
  110. if(NOT CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0)
  111. list(APPEND compile_options
  112. "-Wno-format-overflow"
  113. "-Wno-stringop-truncation"
  114. "-Wno-misleading-indentation"
  115. "-Wno-cast-function-type"
  116. "-Wno-implicit-fallthrough"
  117. "-Wno-unused-const-variable"
  118. "-Wno-switch-unreachable"
  119. "-Wno-format-truncation"
  120. "-Wno-memset-elt-size"
  121. "-Wno-int-in-bool-context"
  122. )
  123. endif()
  124. endif()
  125. # Stack protection
  126. if(NOT BOOTLOADER_BUILD)
  127. if(CONFIG_STACK_CHECK_NORM)
  128. list(APPEND compile_options "-fstack-protector")
  129. elseif(CONFIG_STACK_CHECK_STRONG)
  130. list(APPEND compile_options "-fstack-protector-strong")
  131. elseif(CONFIG_STACK_CHECK_ALL)
  132. list(APPEND compile_options "-fstack-protector-all")
  133. endif()
  134. endif()
  135. if(CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED)
  136. list(APPEND compile_definitions "NDEBUG")
  137. endif()
  138. # Always generate debug symbols (even in Release mode, these don't
  139. # go into the final binary so have no impact on size)
  140. list(APPEND compile_options "-ggdb")
  141. # Use EXTRA_CFLAGS, EXTRA_CXXFLAGS and EXTRA_CPPFLAGS to add more priority options to the compiler
  142. # EXTRA_CPPFLAGS is used for both C and C++
  143. # Unlike environments' CFLAGS/CXXFLAGS/CPPFLAGS which work for both host and target build,
  144. # these works only for target build
  145. set(EXTRA_CFLAGS "$ENV{EXTRA_CFLAGS}")
  146. set(EXTRA_CXXFLAGS "$ENV{EXTRA_CXXFLAGS}")
  147. set(EXTRA_CPPFLAGS "$ENV{EXTRA_CPPFLAGS}")
  148. spaces2list(EXTRA_CFLAGS)
  149. spaces2list(EXTRA_CXXFLAGS)
  150. spaces2list(EXTRA_CPPFLAGS)
  151. list(APPEND c_compile_options ${EXTRA_CFLAGS})
  152. list(APPEND cxx_compile_options ${EXTRA_CXXFLAGS})
  153. list(APPEND compile_options ${EXTRA_CPPFLAGS})
  154. set_default(IDF_COMPILE_DEFINITIONS "${compile_definitions}")
  155. set_default(IDF_COMPILE_OPTIONS "${compile_options}")
  156. set_default(IDF_C_COMPILE_OPTIONS "${c_compile_options}")
  157. set_default(IDF_CXX_COMPILE_OPTIONS "${cxx_compile_options}")
  158. set_default(IDF_INCLUDE_DIRECTORIES "${CONFIG_DIR}")
  159. set(IDF_COMPILE_DEFINITIONS ${IDF_COMPILE_DEFINITIONS} PARENT_SCOPE)
  160. set(IDF_COMPILE_OPTIONS ${IDF_COMPILE_OPTIONS} PARENT_SCOPE)
  161. set(IDF_C_COMPILE_OPTIONS ${IDF_C_COMPILE_OPTIONS} PARENT_SCOPE)
  162. set(IDF_CXX_COMPILE_OPTIONS ${IDF_CXX_COMPILE_OPTIONS} PARENT_SCOPE)
  163. set(IDF_INCLUDE_DIRECTORIES ${CONFIG_DIR} PARENT_SCOPE)
  164. endfunction()
  165. # Verify the IDF environment is configured correctly (environment, toolchain, etc)
  166. function(idf_verify_environment)
  167. if(NOT CMAKE_PROJECT_NAME)
  168. message(FATAL_ERROR "Internal error, IDF project.cmake should have set this variable already")
  169. endif()
  170. # Check toolchain is configured properly in cmake
  171. if(NOT ( ${CMAKE_SYSTEM_NAME} STREQUAL "Generic" AND ${CMAKE_C_COMPILER} MATCHES xtensa))
  172. message(FATAL_ERROR "Internal error, toolchain has not been set correctly by project "
  173. "(or an invalid CMakeCache.txt file has been generated somehow)")
  174. endif()
  175. #
  176. # Warn if the toolchain version doesn't match
  177. #
  178. # TODO: make these platform-specific for diff toolchains
  179. get_expected_ctng_version(expected_toolchain expected_gcc)
  180. gcc_version_check("${expected_gcc}")
  181. crosstool_version_check("${expected_toolchain}")
  182. endfunction()
  183. # idf_get_git_revision
  184. #
  185. # Set global IDF_VER to the git revision of ESP-IDF.
  186. #
  187. # Running git_describe() here automatically triggers rebuilds
  188. # if the ESP-IDF git version changes
  189. function(idf_get_git_revision)
  190. git_describe(IDF_VER_GIT "${IDF_PATH}")
  191. if(EXISTS "${IDF_PATH}/version.txt")
  192. file(STRINGS "${IDF_PATH}/version.txt" IDF_VER_T)
  193. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/version.txt")
  194. else()
  195. set(IDF_VER_T ${IDF_VER_GIT})
  196. endif()
  197. # cut IDF_VER to required 32 characters.
  198. string(SUBSTRING "${IDF_VER_T}" 0 31 IDF_VER)
  199. message(STATUS "IDF_VER: ${IDF_VER}")
  200. add_definitions(-DIDF_VER=\"${IDF_VER}\")
  201. git_submodule_check("${IDF_PATH}")
  202. set(IDF_VER ${IDF_VER} PARENT_SCOPE)
  203. endfunction()
  204. # app_get_revision
  205. #
  206. # Set global PROJECT_VER
  207. #
  208. # If PROJECT_VER variable set in project CMakeLists.txt file, its value will be used.
  209. # Else, if the _project_path/version.txt exists, its contents will be used as PROJECT_VER.
  210. # Else, if the project is located inside a Git repository, the output of git describe will be used.
  211. # Otherwise, PROJECT_VER will be "1".
  212. function(app_get_revision _project_path)
  213. if(NOT DEFINED PROJECT_VER)
  214. if(EXISTS "${_project_path}/version.txt")
  215. file(STRINGS "${_project_path}/version.txt" PROJECT_VER)
  216. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_project_path}/version.txt")
  217. else()
  218. git_describe(PROJECT_VER_GIT "${_project_path}")
  219. if(PROJECT_VER_GIT)
  220. set(PROJECT_VER ${PROJECT_VER_GIT})
  221. else()
  222. message(STATUS "Project is not inside a git repository, \
  223. will not use 'git describe' to determine PROJECT_VER.")
  224. set(PROJECT_VER "1")
  225. endif()
  226. endif()
  227. endif()
  228. message(STATUS "Project version: ${PROJECT_VER}")
  229. set(PROJECT_VER ${PROJECT_VER} PARENT_SCOPE)
  230. endfunction()
  231. # idf_link_components
  232. #
  233. # Link library components to the target
  234. function(idf_link_components target components)
  235. foreach(component ${components})
  236. component_get_target(component_target ${component})
  237. # Add each component library's link-time dependencies (which are otherwise ignored) to the executable
  238. # LINK_DEPENDS in order to trigger a re-link when needed (on Ninja/Makefile generators at least).
  239. # (maybe this should probably be something CMake does, but it doesn't do it...)
  240. if(TARGET ${component_target})
  241. get_target_property(type ${component_target} TYPE)
  242. get_target_property(imported ${component_target} IMPORTED)
  243. if(NOT imported)
  244. if(${type} STREQUAL STATIC_LIBRARY OR ${type} STREQUAL EXECUTABLE)
  245. get_target_property(link_depends "${component_target}" LINK_DEPENDS)
  246. if(link_depends)
  247. set_property(TARGET ${target} APPEND PROPERTY LINK_DEPENDS "${link_depends}")
  248. endif()
  249. endif()
  250. endif()
  251. if(${type} MATCHES .+_LIBRARY)
  252. list(APPEND libraries ${component_target})
  253. endif()
  254. endif()
  255. endforeach()
  256. if(libraries)
  257. # gc-sections is necessary for linking some IDF binary libraries
  258. # (and without it, IDF apps are much larger than they should be)
  259. target_link_libraries(${target} "-Wl,--gc-sections")
  260. target_link_libraries(${target} "-Wl,--start-group")
  261. target_link_libraries(${target} ${libraries})
  262. message(STATUS "Component libraries: ${IDF_COMPONENT_LIBRARIES}")
  263. endif()
  264. endfunction()
  265. # idf_import_components
  266. #
  267. # Adds ESP-IDF as a subdirectory to the current project and imports the components
  268. macro(idf_import_components var idf_path build_path)
  269. #
  270. # Set variables that control the build configuration and the build itself
  271. #
  272. idf_set_variables()
  273. kconfig_set_variables()
  274. #
  275. # Generate a component dependencies file, enumerating components to be included in the build
  276. # as well as their dependencies.
  277. #
  278. execute_process(COMMAND "${CMAKE_COMMAND}"
  279. -D "COMPONENTS=${IDF_COMPONENTS}"
  280. -D "COMPONENT_REQUIRES_COMMON=${IDF_COMPONENT_REQUIRES_COMMON}"
  281. -D "EXCLUDE_COMPONENTS=${IDF_EXCLUDE_COMPONENTS}"
  282. -D "TEST_COMPONENTS=${IDF_TEST_COMPONENTS}"
  283. -D "TEST_EXCLUDE_COMPONENTS=${IDF_TEST_EXCLUDE_COMPONENTS}"
  284. -D "BUILD_TESTS=${IDF_BUILD_TESTS}"
  285. -D "DEPENDENCIES_FILE=${CMAKE_BINARY_DIR}/component_depends.cmake"
  286. -D "COMPONENT_DIRS=${IDF_COMPONENT_DIRS}"
  287. -D "BOOTLOADER_BUILD=${BOOTLOADER_BUILD}"
  288. -D "IDF_TARGET=${IDF_TARGET}"
  289. -D "IDF_PATH=${IDF_PATH}"
  290. -D "DEBUG=${DEBUG}"
  291. -P "${IDF_PATH}/tools/cmake/scripts/expand_requirements.cmake"
  292. WORKING_DIRECTORY "${PROJECT_PATH}"
  293. RESULT_VARIABLE expand_requirements_result)
  294. if(expand_requirements_result)
  295. message(FATAL_ERROR "Failed to expand component requirements")
  296. endif()
  297. include("${CMAKE_BINARY_DIR}/component_depends.cmake")
  298. #
  299. # We now have the following component-related variables:
  300. #
  301. # IDF_COMPONENTS is the list of initial components set by the user
  302. # (or empty to include all components in the build).
  303. # BUILD_COMPONENTS is the list of components to include in the build.
  304. # BUILD_COMPONENT_PATHS is the paths to all of these components, obtained from the component dependencies file.
  305. #
  306. # Print the list of found components and test components
  307. #
  308. string(REPLACE ";" " " BUILD_COMPONENTS_SPACES "${BUILD_COMPONENTS}")
  309. message(STATUS "Component names: ${BUILD_COMPONENTS_SPACES}")
  310. unset(BUILD_COMPONENTS_SPACES)
  311. message(STATUS "Component paths: ${BUILD_COMPONENT_PATHS}")
  312. # Print list of test components
  313. if(TESTS_ALL EQUAL 1 OR TEST_COMPONENTS)
  314. string(REPLACE ";" " " BUILD_TEST_COMPONENTS_SPACES "${BUILD_TEST_COMPONENTS}")
  315. message(STATUS "Test component names: ${BUILD_TEST_COMPONENTS_SPACES}")
  316. unset(BUILD_TEST_COMPONENTS_SPACES)
  317. message(STATUS "Test component paths: ${BUILD_TEST_COMPONENT_PATHS}")
  318. endif()
  319. # Generate project configuration
  320. kconfig_process_config()
  321. # Include sdkconfig.cmake so rest of the build knows the configuration
  322. include(${SDKCONFIG_CMAKE})
  323. # Verify the environment is configured correctly
  324. idf_verify_environment()
  325. # Check git revision (may trigger reruns of cmake)
  326. ## sets IDF_VER to IDF git revision
  327. idf_get_git_revision()
  328. # Check that the targets set in cache, sdkconfig, and in environment all match
  329. idf_check_config_target()
  330. ## get PROJECT_VER
  331. if(NOT BOOTLOADER_BUILD)
  332. app_get_revision("${CMAKE_SOURCE_DIR}")
  333. endif()
  334. # Add some idf-wide definitions
  335. idf_set_global_compile_options()
  336. # generate compile_commands.json (needs to come after project)
  337. set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
  338. #
  339. # Setup variables for linker script generation
  340. #
  341. ldgen_set_variables()
  342. # Include any top-level project_include.cmake files from components
  343. foreach(component ${BUILD_COMPONENT_PATHS})
  344. set(COMPONENT_PATH "${component}")
  345. include_if_exists("${component}/project_include.cmake")
  346. unset(COMPONENT_PATH)
  347. endforeach()
  348. add_subdirectory(${idf_path} ${build_path})
  349. if(IDF_BUILD_ARTIFACTS)
  350. # Write project description JSON file
  351. make_json_list("${BUILD_COMPONENTS}" build_components_json)
  352. make_json_list("${BUILD_COMPONENT_PATHS}" build_component_paths_json)
  353. configure_file("${IDF_PATH}/tools/cmake/project_description.json.in"
  354. "${IDF_BUILD_ARTIFACTS_DIR}/project_description.json")
  355. unset(build_components_json)
  356. unset(build_component_paths_json)
  357. endif()
  358. ldgen_add_dependencies()
  359. set(${var} ${BUILD_COMPONENTS})
  360. endmacro()