utilities.cmake 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # set_default
  2. #
  3. # Define a variable to a default value if otherwise unset.
  4. #
  5. # Priority for new value is:
  6. # - Existing cmake value (ie set with cmake -D, or already set in CMakeLists)
  7. # - Value of any non-empty environment variable of the same name
  8. # - Default value as provided to function
  9. #
  10. function(set_default variable default_value)
  11. if(NOT ${variable})
  12. if(DEFINED ENV{${variable}} AND NOT "$ENV{${variable}}" STREQUAL "")
  13. set(${variable} $ENV{${variable}} PARENT_SCOPE)
  14. else()
  15. set(${variable} ${default_value} PARENT_SCOPE)
  16. endif()
  17. endif()
  18. endfunction()
  19. # spaces2list
  20. #
  21. # Take a variable whose value was space-delimited values, convert to a cmake
  22. # list (semicolon-delimited)
  23. #
  24. # Note: if using this for directories, keeps the issue in place that
  25. # directories can't contain spaces...
  26. #
  27. # TODO: look at cmake separate_arguments, which is quote-aware
  28. function(spaces2list variable_name)
  29. string(REPLACE " " ";" tmp "${${variable_name}}")
  30. set("${variable_name}" "${tmp}" PARENT_SCOPE)
  31. endfunction()
  32. # lines2list
  33. #
  34. # Take a variable with multiple lines of output in it, convert it
  35. # to a cmake list (semicolon-delimited), one line per item
  36. #
  37. function(lines2list variable_name)
  38. string(REGEX REPLACE "\r?\n" ";" tmp "${${variable_name}}")
  39. string(REGEX REPLACE ";;" ";" tmp "${tmp}")
  40. set("${variable_name}" "${tmp}" PARENT_SCOPE)
  41. endfunction()
  42. # move_if_different
  43. #
  44. # If 'source' has different md5sum to 'destination' (or destination
  45. # does not exist, move it across.
  46. #
  47. # If 'source' has the same md5sum as 'destination', delete 'source'.
  48. #
  49. # Avoids timestamp updates for re-generated files where content hasn't
  50. # changed.
  51. function(move_if_different source destination)
  52. set(do_copy 1)
  53. file(GLOB dest_exists ${destination})
  54. if(dest_exists)
  55. file(MD5 ${source} source_md5)
  56. file(MD5 ${destination} dest_md5)
  57. if(source_md5 STREQUAL dest_md5)
  58. set(do_copy "")
  59. endif()
  60. endif()
  61. if(do_copy)
  62. message("Moving ${source} -> ${destination}")
  63. file(RENAME ${source} ${destination})
  64. else()
  65. message("Not moving ${source} -> ${destination}")
  66. file(REMOVE ${source})
  67. endif()
  68. endfunction()
  69. # target_add_binary_data adds binary data into the built target,
  70. # by converting it to a generated source file which is then compiled
  71. # to a binary object as part of the build
  72. function(target_add_binary_data target embed_file embed_type)
  73. get_filename_component(embed_file "${embed_file}" ABSOLUTE)
  74. get_filename_component(name "${embed_file}" NAME)
  75. set(embed_srcfile "${IDF_BUILD_ARTIFACTS_DIR}/${name}.S")
  76. add_custom_command(OUTPUT "${embed_srcfile}"
  77. COMMAND "${CMAKE_COMMAND}"
  78. -D "DATA_FILE=${embed_file}"
  79. -D "SOURCE_FILE=${embed_srcfile}"
  80. -D "FILE_TYPE=${embed_type}"
  81. -P "${IDF_PATH}/tools/cmake/scripts/data_file_embed_asm.cmake"
  82. MAIN_DEPENDENCY "${embed_file}"
  83. DEPENDS "${IDF_PATH}/tools/cmake/scripts/data_file_embed_asm.cmake"
  84. WORKING_DIRECTORY "${IDF_BUILD_ARTIFACTS_DIR}"
  85. VERBATIM)
  86. set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${embed_srcfile}")
  87. target_sources("${target}" PRIVATE "${embed_srcfile}")
  88. endfunction()
  89. macro(include_if_exists path)
  90. if(EXISTS "${path}")
  91. include("${path}")
  92. endif()
  93. endmacro()
  94. # Append a single line to the file specified
  95. # The line ending is determined by the host OS
  96. function(file_append_line file line)
  97. if(DEFINED ENV{MSYSTEM} OR CMAKE_HOST_WIN32)
  98. set(line_ending "\r\n")
  99. else() # unix
  100. set(line_ending "\n")
  101. endif()
  102. file(READ ${file} existing)
  103. string(FIND ${existing} ${line_ending} last_newline REVERSE)
  104. string(LENGTH ${existing} length)
  105. math(EXPR length "${length}-1")
  106. if(NOT length EQUAL last_newline) # file doesn't end with a newline
  107. file(APPEND "${file}" "${line_ending}")
  108. endif()
  109. file(APPEND "${file}" "${line}${line_ending}")
  110. endfunction()
  111. # Add one or more linker scripts to the target, including a link-time dependency
  112. #
  113. # Automatically adds a -L search path for the containing directory (if found),
  114. # and then adds -T with the filename only. This allows INCLUDE directives to be
  115. # used to include other linker scripts in the same directory.
  116. function(target_linker_script target)
  117. foreach(scriptfile ${ARGN})
  118. get_filename_component(abs_script "${scriptfile}" ABSOLUTE)
  119. message(STATUS "Adding linker script ${abs_script}")
  120. get_filename_component(search_dir "${abs_script}" DIRECTORY)
  121. get_filename_component(scriptname "${abs_script}" NAME)
  122. get_target_property(link_libraries "${target}" LINK_LIBRARIES)
  123. list(FIND "${link_libraries}" "-L ${search_dir}" found_search_dir)
  124. if(found_search_dir EQUAL "-1") # not already added as a search path
  125. target_link_libraries("${target}" "-L ${search_dir}")
  126. endif()
  127. target_link_libraries("${target}" "-T ${scriptname}")
  128. # Note: In ESP-IDF, most targets are libraries and libary LINK_DEPENDS don't propagate to
  129. # executable(s) the library is linked to. This is done manually in components.cmake.
  130. set_property(TARGET "${target}" APPEND PROPERTY LINK_DEPENDS "${abs_script}")
  131. endforeach()
  132. endfunction()
  133. # Convert a CMake list to a JSON list and store it in a variable
  134. function(make_json_list list variable)
  135. string(REPLACE ";" "\", \"" result "[ \"${list}\" ]")
  136. set("${variable}" "${result}" PARENT_SCOPE)
  137. endfunction()
  138. # add_prefix
  139. #
  140. # Adds a prefix to each item in the specified list.
  141. #
  142. function(add_prefix var prefix)
  143. foreach(elm ${ARGN})
  144. list(APPEND newlist "${prefix}${elm}")
  145. endforeach()
  146. set(${var} "${newlist}" PARENT_SCOPE)
  147. endfunction()
  148. # fail_at_build_time
  149. #
  150. # Creates a phony target which fails the build and touches CMakeCache.txt to cause a cmake run next time.
  151. #
  152. # This is used when a missing file is required at CMake runtime, but we can't fail the build if it is not found,
  153. # because the "menuconfig" target may be required to fix the problem.
  154. #
  155. # We cannot use CMAKE_CONFIGURE_DEPENDS instead because it only works for files which exist at CMake runtime.
  156. #
  157. function(fail_at_build_time target_name message_line0)
  158. set(message_lines COMMAND ${CMAKE_COMMAND} -E echo "${message_line0}")
  159. foreach(message_line ${ARGN})
  160. set(message_lines ${message_lines} COMMAND ${CMAKE_COMMAND} -E echo "${message_line}")
  161. endforeach()
  162. add_custom_target(${target_name} ALL
  163. ${message_lines}
  164. COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/CMakeCache.txt"
  165. COMMAND ${CMAKE_COMMAND} -P ${IDF_PATH}/tools/cmake/scripts/fail.cmake
  166. VERBATIM)
  167. endfunction()