CMakeIOSInstallCombined.cmake 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. # Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. # file Copyright.txt or https://cmake.org/licensing for details.
  3. # Function to print messages of this module
  4. function(_ios_install_combined_message)
  5. message("[iOS combined] " ${ARGN})
  6. endfunction()
  7. # Get build settings for the current target/config/SDK by running
  8. # `xcodebuild -sdk ... -showBuildSettings` and parsing it's output
  9. function(_ios_install_combined_get_build_setting sdk variable resultvar)
  10. if("${sdk}" STREQUAL "")
  11. message(FATAL_ERROR "`sdk` is empty")
  12. endif()
  13. if("${variable}" STREQUAL "")
  14. message(FATAL_ERROR "`variable` is empty")
  15. endif()
  16. if("${resultvar}" STREQUAL "")
  17. message(FATAL_ERROR "`resultvar` is empty")
  18. endif()
  19. set(
  20. cmd
  21. xcodebuild -showBuildSettings
  22. -sdk "${sdk}"
  23. -target "${CURRENT_TARGET}"
  24. -config "${CURRENT_CONFIG}"
  25. )
  26. execute_process(
  27. COMMAND ${cmd}
  28. WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
  29. RESULT_VARIABLE result
  30. OUTPUT_VARIABLE output
  31. )
  32. if(NOT result EQUAL 0)
  33. message(FATAL_ERROR "Command failed (${result}): ${cmd}")
  34. endif()
  35. if(NOT output MATCHES " ${variable} = ([^\n]*)")
  36. if("${variable}" STREQUAL "VALID_ARCHS")
  37. # VALID_ARCHS may be unset by user for given SDK
  38. # (e.g. for build without simulator).
  39. set("${resultvar}" "" PARENT_SCOPE)
  40. return()
  41. else()
  42. message(FATAL_ERROR "${variable} not found.")
  43. endif()
  44. endif()
  45. set("${resultvar}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
  46. endfunction()
  47. # Get architectures of given SDK (iphonesimulator/iphoneos)
  48. function(_ios_install_combined_get_valid_archs sdk resultvar)
  49. cmake_policy(SET CMP0007 NEW)
  50. if("${resultvar}" STREQUAL "")
  51. message(FATAL_ERROR "`resultvar` is empty")
  52. endif()
  53. _ios_install_combined_get_build_setting("${sdk}" "VALID_ARCHS" valid_archs)
  54. separate_arguments(valid_archs)
  55. list(REMOVE_ITEM valid_archs "") # remove empty elements
  56. list(REMOVE_DUPLICATES valid_archs)
  57. string(REPLACE ";" " " printable "${valid_archs}")
  58. _ios_install_combined_message("Architectures (${sdk}): ${printable}")
  59. set("${resultvar}" "${valid_archs}" PARENT_SCOPE)
  60. endfunction()
  61. # Final target can contain more architectures that specified by SDK. This
  62. # function will run 'lipo -info' and parse output. Result will be returned
  63. # as a CMake list.
  64. function(_ios_install_combined_get_real_archs filename resultvar)
  65. set(cmd "${_lipo_path}" -info "${filename}")
  66. execute_process(
  67. COMMAND ${cmd}
  68. RESULT_VARIABLE result
  69. OUTPUT_VARIABLE output
  70. ERROR_VARIABLE output
  71. OUTPUT_STRIP_TRAILING_WHITESPACE
  72. ERROR_STRIP_TRAILING_WHITESPACE
  73. )
  74. if(NOT result EQUAL 0)
  75. message(
  76. FATAL_ERROR "Command failed (${result}): ${cmd}\n\nOutput:\n${output}"
  77. )
  78. endif()
  79. if(NOT output MATCHES "(Architectures in the fat file: [^\n]+ are|Non-fat file: [^\n]+ is architecture): ([^\n]*)")
  80. message(FATAL_ERROR "Could not detect architecture from: ${output}")
  81. endif()
  82. separate_arguments(CMAKE_MATCH_2)
  83. set(${resultvar} ${CMAKE_MATCH_2} PARENT_SCOPE)
  84. endfunction()
  85. # Run build command for the given SDK
  86. function(_ios_install_combined_build sdk)
  87. if("${sdk}" STREQUAL "")
  88. message(FATAL_ERROR "`sdk` is empty")
  89. endif()
  90. _ios_install_combined_message("Build `${CURRENT_TARGET}` for `${sdk}`")
  91. execute_process(
  92. COMMAND
  93. "${CMAKE_COMMAND}"
  94. --build
  95. .
  96. --target "${CURRENT_TARGET}"
  97. --config ${CURRENT_CONFIG}
  98. --
  99. -sdk "${sdk}"
  100. WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
  101. RESULT_VARIABLE result
  102. )
  103. if(NOT result EQUAL 0)
  104. message(FATAL_ERROR "Build failed")
  105. endif()
  106. endfunction()
  107. # Remove given architecture from file. This step needed only in rare cases
  108. # when target was built in "unusual" way. Emit warning message.
  109. function(_ios_install_combined_remove_arch lib arch)
  110. _ios_install_combined_message(
  111. "Warning! Unexpected architecture `${arch}` detected and will be removed "
  112. "from file `${lib}`")
  113. set(cmd "${_lipo_path}" -remove ${arch} -output ${lib} ${lib})
  114. execute_process(
  115. COMMAND ${cmd}
  116. RESULT_VARIABLE result
  117. OUTPUT_VARIABLE output
  118. ERROR_VARIABLE output
  119. OUTPUT_STRIP_TRAILING_WHITESPACE
  120. ERROR_STRIP_TRAILING_WHITESPACE
  121. )
  122. if(NOT result EQUAL 0)
  123. message(
  124. FATAL_ERROR "Command failed (${result}): ${cmd}\n\nOutput:\n${output}"
  125. )
  126. endif()
  127. endfunction()
  128. # Check that 'lib' contains only 'archs' architectures (remove others).
  129. function(_ios_install_combined_keep_archs lib archs)
  130. _ios_install_combined_get_real_archs("${lib}" real_archs)
  131. set(archs_to_remove ${real_archs})
  132. list(REMOVE_ITEM archs_to_remove ${archs})
  133. foreach(x ${archs_to_remove})
  134. _ios_install_combined_remove_arch("${lib}" "${x}")
  135. endforeach()
  136. endfunction()
  137. function(_ios_install_combined_detect_sdks this_sdk_var corr_sdk_var)
  138. cmake_policy(SET CMP0057 NEW)
  139. set(this_sdk "$ENV{PLATFORM_NAME}")
  140. if("${this_sdk}" STREQUAL "")
  141. message(FATAL_ERROR "Environment variable PLATFORM_NAME is empty")
  142. endif()
  143. set(all_platforms "$ENV{SUPPORTED_PLATFORMS}")
  144. if("${all_platforms}" STREQUAL "")
  145. message(FATAL_ERROR "Environment variable SUPPORTED_PLATFORMS is empty")
  146. endif()
  147. separate_arguments(all_platforms)
  148. if(NOT this_sdk IN_LIST all_platforms)
  149. message(FATAL_ERROR "`${this_sdk}` not found in `${all_platforms}`")
  150. endif()
  151. list(REMOVE_ITEM all_platforms "" "${this_sdk}")
  152. list(LENGTH all_platforms all_platforms_length)
  153. if(NOT all_platforms_length EQUAL 1)
  154. message(FATAL_ERROR "Expected one element: ${all_platforms}")
  155. endif()
  156. set(${this_sdk_var} "${this_sdk}" PARENT_SCOPE)
  157. set(${corr_sdk_var} "${all_platforms}" PARENT_SCOPE)
  158. endfunction()
  159. # Create combined binary for the given target.
  160. #
  161. # Preconditions:
  162. # * Target already installed at ${destination}
  163. # for the ${PLATFORM_NAME} platform
  164. #
  165. # This function will:
  166. # * Run build for the lacking platform, i.e. opposite to the ${PLATFORM_NAME}
  167. # * Fuse both libraries by running lipo
  168. function(ios_install_combined target destination)
  169. if("${target}" STREQUAL "")
  170. message(FATAL_ERROR "`target` is empty")
  171. endif()
  172. if("${destination}" STREQUAL "")
  173. message(FATAL_ERROR "`destination` is empty")
  174. endif()
  175. if(NOT IS_ABSOLUTE "${destination}")
  176. message(FATAL_ERROR "`destination` is not absolute: ${destination}")
  177. endif()
  178. if(IS_DIRECTORY "${destination}" OR IS_SYMLINK "${destination}")
  179. message(FATAL_ERROR "`destination` is no regular file: ${destination}")
  180. endif()
  181. if("${CMAKE_BINARY_DIR}" STREQUAL "")
  182. message(FATAL_ERROR "`CMAKE_BINARY_DIR` is empty")
  183. endif()
  184. if(NOT IS_DIRECTORY "${CMAKE_BINARY_DIR}")
  185. message(FATAL_ERROR "Is not a directory: ${CMAKE_BINARY_DIR}")
  186. endif()
  187. if("${CMAKE_INSTALL_CONFIG_NAME}" STREQUAL "")
  188. message(FATAL_ERROR "CMAKE_INSTALL_CONFIG_NAME is empty")
  189. endif()
  190. set(cmd xcrun -f lipo)
  191. # Do not merge OUTPUT_VARIABLE and ERROR_VARIABLE since latter may contain
  192. # some diagnostic information even for the successful run.
  193. execute_process(
  194. COMMAND ${cmd}
  195. RESULT_VARIABLE result
  196. OUTPUT_VARIABLE output
  197. ERROR_VARIABLE error_output
  198. OUTPUT_STRIP_TRAILING_WHITESPACE
  199. ERROR_STRIP_TRAILING_WHITESPACE
  200. )
  201. if(NOT result EQUAL 0)
  202. message(
  203. FATAL_ERROR "Command failed (${result}): ${cmd}\n\nOutput:\n${output}\nOutput(error):\n${error_output}"
  204. )
  205. endif()
  206. set(_lipo_path ${output})
  207. list(LENGTH _lipo_path len)
  208. if(NOT len EQUAL 1)
  209. message(FATAL_ERROR "Unexpected xcrun output: ${_lipo_path}")
  210. endif()
  211. if(NOT EXISTS "${_lipo_path}")
  212. message(FATAL_ERROR "File not found: ${_lipo_path}")
  213. endif()
  214. set(CURRENT_CONFIG "${CMAKE_INSTALL_CONFIG_NAME}")
  215. set(CURRENT_TARGET "${target}")
  216. _ios_install_combined_message("Target: ${CURRENT_TARGET}")
  217. _ios_install_combined_message("Config: ${CURRENT_CONFIG}")
  218. _ios_install_combined_message("Destination: ${destination}")
  219. # Get SDKs
  220. _ios_install_combined_detect_sdks(this_sdk corr_sdk)
  221. # Get architectures of the target
  222. _ios_install_combined_get_valid_archs("${corr_sdk}" corr_valid_archs)
  223. _ios_install_combined_get_valid_archs("${this_sdk}" this_valid_archs)
  224. # Return if there are no valid architectures for the SDK.
  225. # (note that library already installed)
  226. if("${corr_valid_archs}" STREQUAL "")
  227. _ios_install_combined_message(
  228. "No architectures detected for `${corr_sdk}` (skip)"
  229. )
  230. return()
  231. endif()
  232. # Trigger build of corresponding target
  233. _ios_install_combined_build("${corr_sdk}")
  234. # Get location of the library in build directory
  235. _ios_install_combined_get_build_setting(
  236. "${corr_sdk}" "CONFIGURATION_BUILD_DIR" corr_build_dir)
  237. _ios_install_combined_get_build_setting(
  238. "${corr_sdk}" "EXECUTABLE_PATH" corr_executable_path)
  239. set(corr "${corr_build_dir}/${corr_executable_path}")
  240. _ios_install_combined_keep_archs("${corr}" "${corr_valid_archs}")
  241. _ios_install_combined_keep_archs("${destination}" "${this_valid_archs}")
  242. _ios_install_combined_message("Current: ${destination}")
  243. _ios_install_combined_message("Corresponding: ${corr}")
  244. set(cmd "${_lipo_path}" -create ${corr} ${destination} -output ${destination})
  245. execute_process(
  246. COMMAND ${cmd}
  247. WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
  248. RESULT_VARIABLE result
  249. )
  250. if(NOT result EQUAL 0)
  251. message(FATAL_ERROR "Command failed: ${cmd}")
  252. endif()
  253. _ios_install_combined_message("Install done: ${destination}")
  254. endfunction()