helpers.cmake 18 KB

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