helpers.cmake 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. # OBS CMake macOS helper functions module
  2. # cmake-format: off
  3. # cmake-lint: disable=C0307
  4. # cmake-lint: disable=R0912
  5. # cmake-lint: disable=R0915
  6. # cmake-lint: disable=E1126
  7. # cmake-format: on
  8. include(helpers_common)
  9. # set_target_properties_obs: Set target properties for use in obs-studio
  10. function(set_target_properties_obs target)
  11. set(options "")
  12. set(oneValueArgs "")
  13. set(multiValueArgs PROPERTIES)
  14. cmake_parse_arguments(PARSE_ARGV 0 _STPO "${options}" "${oneValueArgs}" "${multiValueArgs}")
  15. message(DEBUG "Setting additional properties for target ${target}...")
  16. while(_STPO_PROPERTIES)
  17. list(POP_FRONT _STPO_PROPERTIES key value)
  18. set_property(TARGET ${target} PROPERTY ${key} "${value}")
  19. endwhile()
  20. get_target_property(target_type ${target} TYPE)
  21. # Target is a GUI or CLI application
  22. if(target_type STREQUAL EXECUTABLE)
  23. if(target STREQUAL obs-studio)
  24. _check_info_plist()
  25. set_target_properties(
  26. ${target}
  27. PROPERTIES OUTPUT_NAME OBS
  28. MACOSX_BUNDLE TRUE
  29. MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in"
  30. XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.obsproject.obs-studio
  31. XCODE_ATTRIBUTE_PRODUCT_NAME OBS
  32. XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AppIcon
  33. XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY YES
  34. XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY YES
  35. XCODE_EMBED_PLUGINS_REMOVE_HEADERS_ON_COPY YES
  36. XCODE_EMBED_PLUGINS_CODE_SIGN_ON_COPY YES
  37. XCODE_ATTRIBUTE_COPY_PHASE_STRIP NO
  38. XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
  39. get_property(obs_dependencies GLOBAL PROPERTY _OBS_DEPENDENCIES)
  40. add_dependencies(${target} ${obs_dependencies})
  41. if(NOT XCODE)
  42. return()
  43. endif()
  44. get_property(obs_frameworks GLOBAL PROPERTY _OBS_FRAMEWORKS)
  45. set_property(
  46. TARGET ${target}
  47. APPEND
  48. PROPERTY XCODE_EMBED_FRAMEWORKS ${obs_frameworks})
  49. if(SPARKLE_APPCAST_URL AND SPARKLE_PUBLIC_KEY)
  50. set_property(
  51. TARGET ${target}
  52. APPEND
  53. PROPERTY XCODE_EMBED_FRAMEWORKS ${SPARKLE})
  54. endif()
  55. get_property(obs_executables GLOBAL PROPERTY _OBS_EXECUTABLES)
  56. foreach(executable IN LISTS obs_executables)
  57. add_custom_command(
  58. TARGET ${target}
  59. POST_BUILD
  60. COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:${executable}>"
  61. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/MacOS/"
  62. COMMENT "Copy ${executable} to application bundle")
  63. endforeach()
  64. _check_entitlements()
  65. add_custom_command(
  66. TARGET ${target}
  67. POST_BUILD
  68. COMMAND
  69. /usr/bin/sed -i '' 's/font-size: 10pt\;/font-size: 12pt\;/'
  70. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/themes/Acri.qss"
  71. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/themes/Grey.qss"
  72. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/themes/Light.qss"
  73. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/themes/Rachni.qss"
  74. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/themes/Yami.qss"
  75. COMMENT "Patch Qt stylesheets to use larger default font size on macOS")
  76. add_custom_command(
  77. TARGET ${target}
  78. POST_BUILD
  79. COMMAND /bin/ln -fs obs-frontend-api.dylib libobs-frontend-api.1.dylib
  80. WORKING_DIRECTORY "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Frameworks"
  81. COMMENT "Create symlink for legacy obs-frontend-api")
  82. if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/qt.conf")
  83. target_add_resource(${target} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/qt.conf")
  84. endif()
  85. target_add_resource(${target} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Assets.xcassets")
  86. target_add_resource(${target} "${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS")
  87. if(TARGET obs-dal-plugin)
  88. add_custom_command(
  89. TARGET ${target}
  90. POST_BUILD
  91. COMMAND "${CMAKE_COMMAND}" -E copy_directory "$<TARGET_BUNDLE_DIR:obs-dal-plugin>"
  92. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/$<TARGET_BUNDLE_DIR_NAME:obs-dal-plugin>"
  93. COMMENT "Add OBS DAL plugin to application bundle")
  94. endif()
  95. if(TARGET obspython)
  96. add_custom_command(
  97. TARGET ${target}
  98. POST_BUILD
  99. COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE_DIR:obspython>/obspython.py"
  100. "$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources"
  101. COMMENT "Add OBS::python import module")
  102. endif()
  103. _bundle_dependencies(${target})
  104. install(TARGETS ${target} BUNDLE DESTINATION "." COMPONENT Application)
  105. else()
  106. set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target})
  107. set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})
  108. endif()
  109. _add_entitlements()
  110. elseif(target_type STREQUAL SHARED_LIBRARY)
  111. set_target_properties(
  112. ${target}
  113. PROPERTIES NO_SONAME TRUE
  114. MACHO_COMPATIBILITY_VERSION 1.0
  115. MACHO_CURRENT_VERSION ${OBS_VERSION_MAJOR}
  116. SOVERSION 0
  117. VERSION 0
  118. XCODE_ATTRIBUTE_DYLIB_COMPATIBILITY_VERSION 1.0
  119. XCODE_ATTRIBUTE_DYLIB_CURRENT_VERSION ${OBS_VERSION_MAJOR}
  120. XCODE_ATTRIBUTE_PRODUCT_NAME ${target}
  121. XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target})
  122. get_target_property(is_framework ${target} FRAMEWORK)
  123. if(is_framework)
  124. _check_info_plist()
  125. set_target_properties(
  126. ${target}
  127. PROPERTIES FRAMEWORK_VERSION A
  128. MACOSX_FRAMEWORK_IDENTIFIER com.obsproject.${target}
  129. MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in")
  130. endif()
  131. _add_entitlements()
  132. set_property(GLOBAL APPEND PROPERTY _OBS_FRAMEWORKS ${target})
  133. set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})
  134. elseif(target_type STREQUAL MODULE_LIBRARY)
  135. if(target STREQUAL obspython)
  136. set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_NAME ${target}
  137. XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target})
  138. elseif(target STREQUAL obslua)
  139. set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_NAME ${target}
  140. XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target})
  141. elseif(target STREQUAL obs-dal-plugin)
  142. set_target_properties(${target} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
  143. set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})
  144. return()
  145. else()
  146. _check_info_plist()
  147. set_target_properties(
  148. ${target}
  149. PROPERTIES BUNDLE TRUE
  150. BUNDLE_EXTENSION plugin
  151. MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in"
  152. XCODE_ATTRIBUTE_PRODUCT_NAME ${target}
  153. XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target})
  154. if(target STREQUAL obs-browser)
  155. # Good-enough for now as there are no other variants - in _theory_ we should only add the appropriate variant,
  156. # but that is only known at project generation and not build system configuration.
  157. get_target_property(imported_location CEF::Library IMPORTED_LOCATION_RELEASE)
  158. if(imported_location)
  159. list(APPEND cef_items "${imported_location}")
  160. endif()
  161. foreach(helper IN ITEMS _gpu _plugin _renderer "")
  162. if(TARGET OBS::browser-helper${helper})
  163. set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES OBS::browser-helper${helper})
  164. list(APPEND cef_items OBS::browser-helper${helper})
  165. endif()
  166. endforeach()
  167. set_property(GLOBAL APPEND PROPERTY _OBS_FRAMEWORKS ${cef_items})
  168. endif()
  169. endif()
  170. set_property(GLOBAL APPEND PROPERTY OBS_MODULES_ENABLED ${target})
  171. set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})
  172. endif()
  173. target_install_resources(${target})
  174. get_target_property(target_sources ${target} SOURCES)
  175. set(target_ui_files ${target_sources})
  176. list(FILTER target_ui_files INCLUDE REGEX ".+\\.(ui|qrc)")
  177. source_group(
  178. TREE "${CMAKE_CURRENT_SOURCE_DIR}"
  179. PREFIX "UI Files"
  180. FILES ${target_ui_files})
  181. if(${target} STREQUAL libobs)
  182. set(target_source_files ${target_sources})
  183. set(target_header_files ${target_sources})
  184. list(FILTER target_source_files INCLUDE REGEX ".+\\.(m|c[cp]?p?|swift)")
  185. list(FILTER target_header_files INCLUDE REGEX ".+\\.h(pp)?")
  186. source_group(
  187. TREE "${CMAKE_CURRENT_SOURCE_DIR}"
  188. PREFIX "Source Files"
  189. FILES ${target_source_files})
  190. source_group(
  191. TREE "${CMAKE_CURRENT_SOURCE_DIR}"
  192. PREFIX "Header Files"
  193. FILES ${target_header_files})
  194. endif()
  195. endfunction()
  196. # _check_info_plist: Macro to check if project ships with Info.plist template
  197. macro(_check_info_plist)
  198. if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in")
  199. message(FATAL_ERROR "Target ${target} is missing an Info.plist template in its cmake directory.")
  200. endif()
  201. endmacro()
  202. # _check_entitlements: Macro to check if project ships with entitlements plist
  203. macro(_check_entitlements)
  204. if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist")
  205. message(FATAL_ERROR "Target ${target} is missing an entitlements.plist in its cmake directory.")
  206. endif()
  207. endmacro()
  208. # _add_entitlements: Macro to add entitlements shipped with project
  209. macro(_add_entitlements)
  210. if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist")
  211. set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS
  212. "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist")
  213. endif()
  214. endmacro()
  215. # target_export: Helper function to export target as CMake package
  216. function(target_export target)
  217. # Exclude CMake package from 'ALL' target
  218. set(exclude_variant EXCLUDE_FROM_ALL)
  219. _target_export(${target})
  220. endfunction()
  221. # target_install_resources: Helper function to add resources into bundle
  222. function(target_install_resources target)
  223. message(DEBUG "Installing resources for target ${target}...")
  224. if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data")
  225. file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*")
  226. foreach(data_file IN LISTS data_files)
  227. cmake_path(RELATIVE_PATH data_file BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" OUTPUT_VARIABLE
  228. relative_path)
  229. cmake_path(GET relative_path PARENT_PATH relative_path)
  230. target_sources(${target} PRIVATE "${data_file}")
  231. set_property(SOURCE "${data_file}" PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${relative_path}")
  232. source_group("Resources/${relative_path}" FILES "${data_file}")
  233. endforeach()
  234. endif()
  235. endfunction()
  236. # target_add_resource: Helper function to add a specific resource to a bundle
  237. function(target_add_resource target resource)
  238. message(DEBUG "Add resource ${resource} to target ${target} at destination ${destination}...")
  239. target_sources(${target} PRIVATE "${resource}")
  240. set_property(SOURCE "${resource}" PROPERTY MACOSX_PACKAGE_LOCATION Resources)
  241. source_group("Resources" FILES "${resource}")
  242. endfunction()
  243. # _bundle_dependencies: Resolve 3rd party dependencies and add them to macOS app bundle
  244. function(_bundle_dependencies target)
  245. message(DEBUG "Discover dependencies of target ${target}...")
  246. set(found_dependencies)
  247. find_dependencies(TARGET ${target} FOUND_VAR found_dependencies)
  248. get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)
  249. list(LENGTH obs_module_list num_modules)
  250. if(num_modules GREATER 0)
  251. add_dependencies(${target} ${obs_module_list})
  252. set_property(
  253. TARGET ${target}
  254. APPEND
  255. PROPERTY XCODE_EMBED_PLUGINS ${obs_module_list})
  256. foreach(module IN LISTS obs_module_list)
  257. find_dependencies(TARGET ${module} FOUND_VAR found_dependencies)
  258. endforeach()
  259. endif()
  260. list(REMOVE_DUPLICATES found_dependencies)
  261. set(library_paths)
  262. set(plugins_list)
  263. file(GLOB sdk_library_paths /Applications/Xcode*.app)
  264. set(system_library_path "/usr/lib/")
  265. foreach(library IN LISTS found_dependencies)
  266. get_target_property(library_type ${library} TYPE)
  267. get_target_property(is_framework ${library} FRAMEWORK)
  268. get_target_property(is_imported ${library} IMPORTED)
  269. if(is_imported)
  270. get_target_property(imported_location ${library} LOCATION)
  271. if(NOT imported_location)
  272. continue()
  273. endif()
  274. set(is_xcode_framework FALSE)
  275. set(is_system_framework FALSE)
  276. foreach(sdk_library_path IN LISTS sdk_library_paths)
  277. if(is_xcode_framework)
  278. break()
  279. endif()
  280. cmake_path(IS_PREFIX sdk_library_path "${imported_location}" is_xcode_framework)
  281. endforeach()
  282. cmake_path(IS_PREFIX system_library_path "${imported_location}" is_system_framework)
  283. if(is_system_framework OR is_xcode_framework)
  284. continue()
  285. elseif(is_framework)
  286. file(REAL_PATH "../../.." library_location BASE_DIRECTORY "${imported_location}")
  287. elseif(NOT library_type STREQUAL "STATIC_LIBRARY")
  288. if(NOT imported_location MATCHES ".+\\.a")
  289. set(library_location "${imported_location}")
  290. else()
  291. continue()
  292. endif()
  293. else()
  294. continue()
  295. endif()
  296. if(library MATCHES "Qt[56]?::.+")
  297. find_qt_plugins(COMPONENT ${library} TARGET ${target} FOUND_VAR plugins_list)
  298. endif()
  299. list(APPEND library_paths ${library_location})
  300. elseif(NOT imported AND library_type STREQUAL "SHARED_LIBRARY")
  301. message(TRACE "${library} is a project target")
  302. list(APPEND library_paths ${library})
  303. endif()
  304. endforeach()
  305. list(REMOVE_DUPLICATES plugins_list)
  306. foreach(plugin IN LISTS plugins_list)
  307. cmake_path(GET plugin PARENT_PATH plugin_path)
  308. set(plugin_base_dir "${plugin_path}/../")
  309. cmake_path(SET plugin_stem_dir NORMALIZE "${plugin_base_dir}")
  310. cmake_path(RELATIVE_PATH plugin_path BASE_DIRECTORY "${plugin_stem_dir}" OUTPUT_VARIABLE plugin_file_name)
  311. target_sources(${target} PRIVATE "${plugin}")
  312. set_source_files_properties("${plugin}" PROPERTIES MACOSX_PACKAGE_LOCATION "plugins/${plugin_file_name}"
  313. XCODE_FILE_ATTRIBUTES "CodeSignOnCopy")
  314. source_group("Qt plugins" FILES "${plugin}")
  315. endforeach()
  316. list(REMOVE_DUPLICATES library_paths)
  317. set_property(
  318. TARGET ${target}
  319. APPEND
  320. PROPERTY XCODE_EMBED_FRAMEWORKS ${library_paths})
  321. endfunction()