full-build-macos.sh 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. #!/usr/bin/env bash
  2. ##############################################################################
  3. # macOS full build script
  4. ##############################################################################
  5. #
  6. # This script contains all steps necessary to:
  7. #
  8. # * Build OBS with all default plugins and dependencies
  9. # * Create a macOS application bundle
  10. # * Code-sign the macOS application-bundle
  11. # * Package a macOS installation image
  12. # * Notarize macOS application-bundle and/or installation image
  13. #
  14. # Parameters:
  15. # -b: Create macOS bundle
  16. # -d: Skip dependency checks
  17. # -p: Create macOS distribution image
  18. # -n: Notarize macOS app and disk image (implies bundling)
  19. # -s: Skip the build process (useful for bundling/packaging only)
  20. # -h: Print usage help
  21. #
  22. # Environment Variables (optional):
  23. # MACOS_DEPS_VERSION : Pre-compiled macOS dependencies version
  24. # CEF_BUILD_VERSION : Chromium Embedded Framework version
  25. # VLC_VERISON : VLC version
  26. # SPARKLE_VERSION : Sparke Framework version
  27. # BUILD_DIR : Alternative directory to build OBS in
  28. #
  29. ##############################################################################
  30. # Halt on errors
  31. set -eE
  32. ## SET UP ENVIRONMENT ##
  33. PRODUCT_NAME="OBS-Studio"
  34. CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
  35. DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
  36. BUILD_DIR="${BUILD_DIR:-build}"
  37. BUILD_CONFIG=${BUILD_CONFIG:-RelWithDebInfo}
  38. CI_SCRIPTS="${CHECKOUT_DIR}/CI/scripts/macos"
  39. CI_WORKFLOW="${CHECKOUT_DIR}/.github/workflows/main.yml"
  40. CI_CEF_VERSION=$(cat ${CI_WORKFLOW} | sed -En "s/[ ]+CEF_BUILD_VERSION: '([0-9]+)'/\1/p")
  41. CI_DEPS_VERSION=$(cat ${CI_WORKFLOW} | sed -En "s/[ ]+MACOS_DEPS_VERSION: '([0-9\-]+)'/\1/p")
  42. CI_VLC_VERSION=$(cat ${CI_WORKFLOW} | sed -En "s/[ ]+VLC_VERSION: '([0-9\.]+)'/\1/p")
  43. CI_SPARKLE_VERSION=$(cat ${CI_WORKFLOW} | sed -En "s/[ ]+SPARKLE_VERSION: '([0-9\.]+)'/\1/p")
  44. CI_QT_VERSION=$(cat ${CI_WORKFLOW} | sed -En "s/[ ]+QT_VERSION: '([0-9\.]+)'/\1/p" | head -1)
  45. CI_MIN_MACOS_VERSION=$(cat ${CI_WORKFLOW} | sed -En "s/[ ]+MIN_MACOS_VERSION: '([0-9\.]+)'/\1/p")
  46. BUILD_DEPS=(
  47. "obs-deps ${MACOS_DEPS_VERSION:-${CI_DEPS_VERSION}}"
  48. "qt-deps ${QT_VERSION:-${CI_QT_VERSION}} ${MACOS_DEPS_VERSION:-${CI_DEPS_VERSION}}"
  49. "cef ${CEF_BUILD_VERSION:-${CI_CEF_VERSION}}"
  50. "vlc ${VLC_VERSION:-${CI_VLC_VERSION}}"
  51. "sparkle ${SPARKLE_VERSION:-${CI_SPARKLE_VERSION}}"
  52. )
  53. if [ -n "${TERM-}" ]; then
  54. COLOR_RED=$(tput setaf 1)
  55. COLOR_GREEN=$(tput setaf 2)
  56. COLOR_BLUE=$(tput setaf 4)
  57. COLOR_ORANGE=$(tput setaf 3)
  58. COLOR_RESET=$(tput sgr0)
  59. else
  60. COLOR_RED=""
  61. COLOR_GREEN=""
  62. COLOR_BLUE=""
  63. COLOR_ORANGE=""
  64. COLOR_RESET=""
  65. fi
  66. MACOS_VERSION="$(sw_vers -productVersion)"
  67. MACOS_MAJOR="$(echo ${MACOS_VERSION} | cut -d '.' -f 1)"
  68. MACOS_MINOR="$(echo ${MACOS_VERSION} | cut -d '.' -f 2)"
  69. ## DEFINE UTILITIES ##
  70. hr() {
  71. echo -e "${COLOR_BLUE}[${PRODUCT_NAME}] ${1}${COLOR_RESET}"
  72. }
  73. step() {
  74. echo -e "${COLOR_GREEN} + ${1}${COLOR_RESET}"
  75. }
  76. info() {
  77. echo -e "${COLOR_ORANGE} + ${1}${COLOR_RESET}"
  78. }
  79. error() {
  80. echo -e "${COLOR_RED} + ${1}${COLOR_RESET}"
  81. }
  82. exists() {
  83. command -v "$1" >/dev/null 2>&1
  84. }
  85. ensure_dir() {
  86. [[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1}
  87. }
  88. cleanup() {
  89. rm -rf "${CHECKOUT_DIR}/${BUILD_DIR}/settings.json"
  90. unset CODESIGN_IDENT
  91. unset CODESIGN_IDENT_USER
  92. unset CODESIGN_IDENT_PASS
  93. }
  94. caught_error() {
  95. error "ERROR during build step: ${1}"
  96. cleanup
  97. exit 1
  98. }
  99. ## CHECK AND INSTALL DEPENDENCIES ##
  100. check_macos_version() {
  101. MIN_VERSION=${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}}
  102. MIN_MAJOR=$(echo ${MIN_VERSION} | cut -d '.' -f 1)
  103. MIN_MINOR=$(echo ${MIN_VERSION} | cut -d '.' -f 2)
  104. if [ "${MACOS_MAJOR}" -lt "11" ] && [ "${MACOS_MINOR}" -lt "${MIN_MINOR}" ]; then
  105. error "WARNING: Minimum required macOS version is ${MIN_VERSION}, but running on ${MACOS_VERSION}"
  106. fi
  107. }
  108. install_homebrew_deps() {
  109. if ! exists brew; then
  110. error "Homebrew not found - please install homebrew (https://brew.sh)"
  111. exit 1
  112. fi
  113. if [ -d /usr/local/opt/[email protected] ]; then
  114. brew uninstall [email protected]
  115. brew untap local/openssl
  116. fi
  117. if [ -d /usr/local/opt/[email protected] ]; then
  118. brew uninstall [email protected]
  119. brew untap local/python2
  120. fi
  121. brew bundle --file ${CI_SCRIPTS}/Brewfile
  122. check_curl
  123. }
  124. check_curl() {
  125. if [ "${MACOS_MAJOR}" -lt "11" ] && [ "${MACOS_MINOR}" -lt "15" ]; then
  126. if [ ! -d /usr/local/opt/curl ]; then
  127. step "Installing Homebrew curl.."
  128. brew install curl
  129. fi
  130. export CURLCMD="/usr/local/opt/curl/bin/curl"
  131. else
  132. export CURLCMD="curl"
  133. fi
  134. }
  135. check_ccache() {
  136. export PATH=/usr/local/opt/ccache/libexec:${PATH}
  137. CCACHE_STATUS=$(ccache -s >/dev/null 2>&1 && echo "CCache available." || echo "CCache is not available.")
  138. info "${CCACHE_STATUS}"
  139. }
  140. install_obs-deps() {
  141. hr "Setting up pre-built macOS OBS dependencies v${1}"
  142. ensure_dir ${DEPS_BUILD_DIR}
  143. step "Download..."
  144. ${CURLCMD} --progress-bar -L -C - -O https://github.com/obsproject/obs-deps/releases/download/${1}/macos-deps-${1}.tar.gz
  145. step "Unpack..."
  146. tar -xf ./macos-deps-${1}.tar.gz -C /tmp
  147. }
  148. install_qt-deps() {
  149. hr "Setting up pre-built dependency QT v${1}"
  150. ensure_dir ${DEPS_BUILD_DIR}
  151. step "Download..."
  152. ${CURLCMD} --progress-bar -L -C - -O https://github.com/obsproject/obs-deps/releases/download/${2}/macos-qt-${1}-${2}.tar.gz
  153. step "Unpack..."
  154. tar -xf ./macos-qt-${1}-${2}.tar.gz -C /tmp
  155. xattr -r -d com.apple.quarantine /tmp/obsdeps
  156. }
  157. install_vlc() {
  158. hr "Setting up dependency VLC v${1}"
  159. ensure_dir ${DEPS_BUILD_DIR}
  160. step "Download..."
  161. ${CURLCMD} --progress-bar -L -C - -O https://downloads.videolan.org/vlc/${1}/vlc-${1}.tar.xz
  162. step "Unpack ..."
  163. tar -xf vlc-${1}.tar.xz
  164. }
  165. install_sparkle() {
  166. hr "Setting up dependency Sparkle v${1} (might prompt for password)"
  167. ensure_dir ${DEPS_BUILD_DIR}/sparkle
  168. step "Download..."
  169. ${CURLCMD} --progress-bar -L -C - -o sparkle.tar.bz2 https://github.com/sparkle-project/Sparkle/releases/download/${1}/Sparkle-${1}.tar.bz2
  170. step "Unpack..."
  171. tar -xf ./sparkle.tar.bz2
  172. step "Copy to destination..."
  173. if [ -d /Library/Frameworks/Sparkle.framework/ ]; then
  174. info "Warning - Sparkle framework already found in /Library/Frameworks"
  175. else
  176. sudo cp -R ./Sparkle.framework/ /Library/Frameworks/Sparkle.framework/
  177. fi
  178. }
  179. install_cef() {
  180. hr "Building dependency CEF v${1}"
  181. ensure_dir ${DEPS_BUILD_DIR}
  182. step "Download..."
  183. ${CURLCMD} --progress-bar -L -C - -O https://obs-nightly.s3-us-west-2.amazonaws.com/cef_binary_${1}_macosx64.tar.bz2
  184. step "Unpack..."
  185. tar -xf ./cef_binary_${1}_macosx64.tar.bz2
  186. cd ./cef_binary_${1}_macosx64
  187. step "Fix tests..."
  188. sed -i '.orig' '/add_subdirectory(tests\/ceftests)/d' ./CMakeLists.txt
  189. sed -i '.orig' s/\"10.9\"/\"${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}}\"/ ./cmake/cef_variables.cmake
  190. ensure_dir ./build
  191. step "Run CMAKE..."
  192. cmake \
  193. -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++ -Wno-deprecated-declarations"\
  194. -DCMAKE_EXE_LINKER_FLAGS="-std=c++11 -stdlib=libc++"\
  195. -DCMAKE_OSX_DEPLOYMENT_TARGET=${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}} \
  196. ..
  197. step "Build..."
  198. make -j4
  199. if [ ! -d libcef_dll ]; then mkdir libcef_dll; fi
  200. }
  201. ## CHECK AND INSTALL PACKAGING DEPENDENCIES ##
  202. install_dmgbuild() {
  203. if ! exists dmgbuild; then
  204. if exists "pip3"; then
  205. PIPCMD="pip3"
  206. elif exists "pip"; then
  207. PIPCMD="pip"
  208. else
  209. error "Pip not found - please install pip via 'python -m ensurepip'"
  210. exit 1
  211. fi
  212. ${PIPCMD} install dmgbuild
  213. fi
  214. }
  215. ## OBS BUILD FROM SOURCE ##
  216. configure_obs_build() {
  217. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  218. CUR_DATE=$(date +"%Y-%m-%d@%H%M%S")
  219. NIGHTLY_DIR="${CHECKOUT_DIR}/nightly-${CUR_DATE}"
  220. PACKAGE_NAME=$(find . -name "*.dmg")
  221. if [ -d ./OBS.app ]; then
  222. ensure_dir "${NIGHTLY_DIR}"
  223. mv ../${BUILD_DIR}/OBS.app .
  224. info "You can find OBS.app in ${NIGHTLY_DIR}"
  225. fi
  226. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  227. if ([ -n "${PACKAGE_NAME}" ] && [ -f ${PACKAGE_NAME} ]); then
  228. ensure_dir "${NIGHTLY_DIR}"
  229. mv ../${BUILD_DIR}/$(basename "${PACKAGE_NAME}") .
  230. info "You can find ${PACKAGE_NAME} in ${NIGHTLY_DIR}"
  231. fi
  232. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  233. hr "Run CMAKE for OBS..."
  234. cmake -DENABLE_SPARKLE_UPDATER=ON \
  235. -DCMAKE_OSX_DEPLOYMENT_TARGET=${MIN_MACOS_VERSION:-${CI_MIN_MACOS_VERSION}} \
  236. -DDISABLE_PYTHON=ON \
  237. -DQTDIR="/tmp/obsdeps" \
  238. -DSWIGDIR="/tmp/obsdeps" \
  239. -DDepsPath="/tmp/obsdeps" \
  240. -DVLCPath="${DEPS_BUILD_DIR}/vlc-${VLC_VERSION:-${CI_VLC_VERSION}}" \
  241. -DBUILD_BROWSER=ON \
  242. -DBROWSER_DEPLOY=ON \
  243. -DBUILD_CAPTIONS=ON \
  244. -DWITH_RTMPS=ON \
  245. -DCEF_ROOT_DIR="${DEPS_BUILD_DIR}/cef_binary_${CEF_BUILD_VERSION:-${CI_CEF_VERSION}}_macosx64" \
  246. -DCMAKE_BUILD_TYPE="${BUILD_CONFIG}" \
  247. ..
  248. }
  249. run_obs_build() {
  250. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  251. hr "Build OBS..."
  252. make -j4
  253. }
  254. ## OBS BUNDLE AS MACOS APPLICATION ##
  255. bundle_dylibs() {
  256. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  257. if [ ! -d ./OBS.app ]; then
  258. error "No OBS.app bundle found"
  259. exit 1
  260. fi
  261. hr "Bundle dylibs for macOS application"
  262. step "Run dylibBundler.."
  263. ${CI_SCRIPTS}/app/dylibbundler -cd -of -a ./OBS.app -q -f \
  264. -s ./OBS.app/Contents/MacOS \
  265. -s "${DEPS_BUILD_DIR}/sparkle/Sparkle.framework" \
  266. -s ./rundir/${BUILD_CONFIG}/bin/ \
  267. -x ./OBS.app/Contents/PlugIns/coreaudio-encoder.so \
  268. -x ./OBS.app/Contents/PlugIns/decklink-ouput-ui.so \
  269. -x ./OBS.app/Contents/PlugIns/frontend-tools.so \
  270. -x ./OBS.app/Contents/PlugIns/image-source.so \
  271. -x ./OBS.app/Contents/PlugIns/linux-jack.so \
  272. -x ./OBS.app/Contents/PlugIns/mac-avcapture.so \
  273. -x ./OBS.app/Contents/PlugIns/mac-capture.so \
  274. -x ./OBS.app/Contents/PlugIns/mac-decklink.so \
  275. -x ./OBS.app/Contents/PlugIns/mac-syphon.so \
  276. -x ./OBS.app/Contents/PlugIns/mac-vth264.so \
  277. -x ./OBS.app/Contents/PlugIns/obs-browser.so \
  278. -x ./OBS.app/Contents/PlugIns/obs-browser-page \
  279. -x ./OBS.app/Contents/PlugIns/obs-ffmpeg.so \
  280. -x ./OBS.app/Contents/PlugIns/obs-filters.so \
  281. -x ./OBS.app/Contents/PlugIns/obs-transitions.so \
  282. -x ./OBS.app/Contents/PlugIns/obs-vst.so \
  283. -x ./OBS.app/Contents/PlugIns/rtmp-services.so \
  284. -x ./OBS.app/Contents/MacOS/obs-ffmpeg-mux \
  285. -x ./OBS.app/Contents/MacOS/obslua.so \
  286. -x ./OBS.app/Contents/PlugIns/obs-x264.so \
  287. -x ./OBS.app/Contents/PlugIns/text-freetype2.so \
  288. -x ./OBS.app/Contents/PlugIns/obs-libfdk.so \
  289. -x ./OBS.app/Contents/PlugIns/obs-outputs.so
  290. step "Move libobs-opengl to final destination"
  291. if [ -f "./libobs-opengl/libobs-opengl.so" ]; then
  292. cp ./libobs-opengl/libobs-opengl.so ./OBS.app/Contents/Frameworks
  293. else
  294. cp ./libobs-opengl/${BUILD_CONFIG}/libobs-opengl.so ./OBS.app/Contents/Frameworks
  295. fi
  296. step "Copy QtNetwork for plugin support"
  297. cp -R /tmp/obsdeps/lib/QtNetwork.framework ./OBS.app/Contents/Frameworks
  298. chmod -R +w ./OBS.app/Contents/Frameworks/QtNetwork.framework
  299. rm -r ./OBS.app/Contents/Frameworks/QtNetwork.framework/Headers
  300. rm -r ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/Headers/
  301. chmod 644 ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/Resources/Info.plist
  302. install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/QtNetwork
  303. install_name_tool -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/QtNetwork
  304. }
  305. install_frameworks() {
  306. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  307. if [ ! -d ./OBS.app ]; then
  308. error "No OBS.app bundle found"
  309. exit 1
  310. fi
  311. hr "Adding Chromium Embedded Framework"
  312. step "Copy Framework..."
  313. cp -R "${DEPS_BUILD_DIR}/cef_binary_${CEF_BUILD_VERSION:-${CI_CEF_VERSION}}_macosx64/Release/Chromium Embedded Framework.framework" ./OBS.app/Contents/Frameworks/
  314. chown -R $(whoami) ./OBS.app/Contents/Frameworks/
  315. }
  316. prepare_macos_bundle() {
  317. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  318. if [ ! -d ./rundir/${BUILD_CONFIG}/bin ]; then
  319. error "No OBS build found"
  320. return
  321. fi
  322. if [ -d ./OBS.app ]; then rm -rf ./OBS.app; fi
  323. hr "Preparing OBS.app bundle"
  324. step "Copy binary and plugins..."
  325. mkdir -p OBS.app/Contents/MacOS
  326. mkdir OBS.app/Contents/PlugIns
  327. mkdir OBS.app/Contents/Resources
  328. cp rundir/${BUILD_CONFIG}/bin/obs ./OBS.app/Contents/MacOS
  329. cp rundir/${BUILD_CONFIG}/bin/obs-ffmpeg-mux ./OBS.app/Contents/MacOS
  330. cp rundir/${BUILD_CONFIG}/bin/libobsglad.0.dylib ./OBS.app/Contents/MacOS
  331. cp -R rundir/${BUILD_CONFIG}/data ./OBS.app/Contents/Resources
  332. cp ${CI_SCRIPTS}/app/obs.icns ./OBS.app/Contents/Resources
  333. cp -R rundir/${BUILD_CONFIG}/obs-plugins/ ./OBS.app/Contents/PlugIns
  334. cp ${CI_SCRIPTS}/app/Info.plist ./OBS.app/Contents
  335. # Scripting plugins are required to be placed in same directory as binary
  336. if [ -d ./OBS.app/Contents/Resources/data/obs-scripting ]; then
  337. mv ./OBS.app/Contents/Resources/data/obs-scripting/obslua.so ./OBS.app/Contents/MacOS/
  338. # mv ./OBS.app/Contents/Resources/data/obs-scripting/_obspython.so ./OBS.app/Contents/MacOS/
  339. # mv ./OBS.app/Contents/Resources/data/obs-scripting/obspython.py ./OBS.app/Contents/MacOS/
  340. rm -rf ./OBS.app/Contents/Resources/data/obs-scripting/
  341. fi
  342. bundle_dylibs
  343. install_frameworks
  344. cp ${CI_SCRIPTS}/app/OBSPublicDSAKey.pem ./OBS.app/Contents/Resources
  345. step "Set bundle meta information..."
  346. plutil -insert CFBundleVersion -string ${GIT_TAG}-${GIT_HASH} ./OBS.app/Contents/Info.plist
  347. plutil -insert CFBundleShortVersionString -string ${GIT_TAG}-${GIT_HASH} ./OBS.app/Contents/Info.plist
  348. plutil -insert OBSFeedsURL -string https://obsproject.com/osx_update/feeds.xml ./OBS.app/Contents/Info.plist
  349. plutil -insert SUFeedURL -string https://obsproject.com/osx_update/stable/updates.xml ./OBS.app/Contents/Info.plist
  350. plutil -insert SUPublicDSAKeyFile -string OBSPublicDSAKey.pem ./OBS.app/Contents/Info.plist
  351. }
  352. ## CREATE MACOS DISTRIBUTION AND INSTALLER IMAGE ##
  353. prepare_macos_image() {
  354. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  355. if [ ! -d ./OBS.app ]; then
  356. error "No OBS.app bundle found"
  357. return
  358. fi
  359. hr "Preparing macOS installation image"
  360. if [ -f "${FILE_NAME}" ]; then
  361. rm "${FILE_NAME}"
  362. fi
  363. step "Run dmgbuild..."
  364. cp "${CI_SCRIPTS}/package/settings.json.template" ./settings.json
  365. sed -i '' 's#\$\$VERSION\$\$#'"${GIT_TAG}"'#g' ./settings.json
  366. sed -i '' 's#\$\$CI_PATH\$\$#'"${CI_SCRIPTS}"'#g' ./settings.json
  367. sed -i '' 's#\$\$BUNDLE_PATH\$\$#'"${CHECKOUT_DIR}"'/build#g' ./settings.json
  368. echo -n "${COLOR_ORANGE}"
  369. dmgbuild "OBS-Studio ${GIT_TAG}" "${FILE_NAME}" -s ./settings.json
  370. echo -n "${COLOR_RESET}"
  371. if [ -n "${CODESIGN_OBS}" ]; then
  372. codesign_image
  373. fi
  374. }
  375. ## SET UP CODE SIGNING AND NOTARIZATION CREDENTIALS ##
  376. ##############################################################################
  377. # Apple Developer Identity needed:
  378. #
  379. # + Signing the code requires a developer identity in the system's keychain
  380. # + codesign will look up and find the identity automatically
  381. #
  382. ##############################################################################
  383. read_codesign_ident() {
  384. if [ ! -n "${CODESIGN_IDENT}" ]; then
  385. step "Code-signing Setup"
  386. read -p "${COLOR_ORANGE} + Apple developer identity: ${COLOR_RESET}" CODESIGN_IDENT
  387. fi
  388. }
  389. ##############################################################################
  390. # Apple Developer credentials necessary:
  391. #
  392. # + Signing for distribution and notarization require an active Apple
  393. # Developer membership
  394. # + An Apple Development identity is needed for code signing
  395. # (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)')
  396. # + Your Apple developer ID is needed for notarization
  397. # + An app-specific password is necessary for notarization from CLI
  398. # + This password will be stored in your macOS keychain under the identifier
  399. # 'OBS-Codesign-Password'with access Apple's 'altool' only.
  400. ##############################################################################
  401. read_codesign_pass() {
  402. if [ ! -n "${CODESIGN_IDENT_PASS}" ]; then
  403. step "Notarization Setup"
  404. read -p "${COLOR_ORANGE} + Apple account id: ${COLOR_RESET}" CODESIGN_IDENT_USER
  405. CODESIGN_IDENT_PASS=$(stty -echo; read -p "${COLOR_ORANGE} + Apple developer password: ${COLOR_RESET}" pwd; stty echo; echo $pwd)
  406. echo -n "${COLOR_ORANGE}"
  407. xcrun altool --store-password-in-keychain-item "OBS-Codesign-Password" -u "${CODESIGN_IDENT_USER}" -p "${CODESIGN_IDENT_PASS}"
  408. echo -n "${COLOR_RESET}"
  409. CODESIGN_IDENT_SHORT=$(echo "${CODESIGN_IDENT}" | sed -En "s/.+\((.+)\)/\1/p")
  410. fi
  411. }
  412. codesign_bundle() {
  413. if [ ! -n "${CODESIGN_OBS}" ]; then step "Skipping application bundle code signing"; return; fi
  414. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  415. trap "caught_error 'code-signing app'" ERR
  416. if [ ! -d ./OBS.app ]; then
  417. error "No OBS.app bundle found"
  418. return
  419. fi
  420. hr "Code-signing application bundle"
  421. xattr -crs ./OBS.app
  422. read_codesign_ident
  423. step "Code-sign Sparkle framework..."
  424. echo -n "${COLOR_ORANGE}"
  425. codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop"
  426. codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate"
  427. codesign --force --options runtime --sign "${CODESIGN_IDENT}" --deep ./OBS.app/Contents/Frameworks/Sparkle.framework
  428. echo -n "${COLOR_RESET}"
  429. step "Code-sign CEF framework..."
  430. echo -n "${COLOR_ORANGE}"
  431. codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libEGL.dylib"
  432. codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libswiftshader_libEGL.dylib"
  433. codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libGLESv2.dylib"
  434. codesign --force --options runtime --sign "${CODESIGN_IDENT}" "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libswiftshader_libGLESv2.dylib"
  435. codesign --force --options runtime --sign "${CODESIGN_IDENT}" --deep "./OBS.app/Contents/Frameworks/Chromium Embedded Framework.framework"
  436. echo -n "${COLOR_RESET}"
  437. step "Code-sign OBS code..."
  438. echo -n "${COLOR_ORANGE}"
  439. codesign --force --options runtime --entitlements "${CI_SCRIPTS}/app/entitlements.plist" --sign "${CODESIGN_IDENT}" --deep ./OBS.app
  440. echo -n "${COLOR_RESET}"
  441. step "Check code-sign result..."
  442. codesign -dvv ./OBS.app
  443. }
  444. codesign_image() {
  445. if [ ! -n "${CODESIGN_OBS}" ]; then step "Skipping installer image code signing"; return; fi
  446. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  447. trap "caught_error 'code-signing image'" ERR
  448. if [ ! -f "${FILE_NAME}" ]; then
  449. error "No OBS disk image found"
  450. return
  451. fi
  452. hr "Code-signing installation image"
  453. read_codesign_ident
  454. step "Code-sign OBS installer image..."
  455. echo -n "${COLOR_ORANGE}";
  456. codesign --force --sign "${CODESIGN_IDENT}" "${FILE_NAME}"
  457. echo -n "${COLOR_RESET}"
  458. step "Check code-sign result..."
  459. codesign -dvv "${FILE_NAME}"
  460. }
  461. ## BUILD FROM SOURCE META FUNCTION ##
  462. full-build-macos() {
  463. if [ -n "${SKIP_BUILD}" ]; then step "Skipping full build"; return; fi
  464. if [ ! -n "${SKIP_DEPS}" ]; then
  465. hr "Installing Homebrew dependencies"
  466. install_homebrew_deps
  467. for DEPENDENCY in "${BUILD_DEPS[@]}"; do
  468. set -- ${DEPENDENCY}
  469. trap "caught_error ${DEPENDENCY}" ERR
  470. FUNC_NAME="install_${1}"
  471. ${FUNC_NAME} ${2} ${3}
  472. done
  473. check_ccache
  474. trap "caught_error 'cmake'" ERR
  475. fi
  476. configure_obs_build
  477. run_obs_build
  478. }
  479. ## BUNDLE MACOS APPLICATION META FUNCTION ##
  480. bundle_macos() {
  481. if [ ! -n "${BUNDLE_OBS}" ]; then step "Skipping application bundle creation"; return; fi
  482. hr "Creating macOS app bundle"
  483. trap "caught_error 'bundle app'" ERR
  484. ensure_dir ${CHECKOUT_DIR}
  485. prepare_macos_bundle
  486. }
  487. ## PACKAGE MACOS DISTRIBUTION IMAGE META FUNCTION ##
  488. package_macos() {
  489. if [ ! -n "${PACKAGE_OBS}" ]; then step "Skipping installer image creation"; return; fi
  490. hr "Creating macOS .dmg image"
  491. trap "caught_error 'package app'" ERR
  492. install_dmgbuild
  493. prepare_macos_image
  494. }
  495. ## NOTARIZATION META FUNCTION ##
  496. notarize_macos() {
  497. if [ ! -n "${NOTARIZE_OBS}" ]; then step "Skipping macOS notarization"; return; fi;
  498. hr "Notarizing OBS for macOS"
  499. trap "caught_error 'notarizing app'" ERR
  500. ensure_dir "${CHECKOUT_DIR}/${BUILD_DIR}"
  501. if [ -f "${FILE_NAME}" ]; then
  502. NOTARIZE_TARGET="${FILE_NAME}"
  503. xcnotary precheck "./OBS.app"
  504. elif [ -d "OBS.app" ]; then
  505. NOTARIZE_TARGET="./OBS.app"
  506. else
  507. error "No notarization app bundle ('OBS.app') or disk image ('${FILE_NAME}') found"
  508. return
  509. fi
  510. if [ "$?" -eq 0 ]; then
  511. read_codesign_ident
  512. read_codesign_pass
  513. step "Run xcnotary with ${NOTARIZE_TARGET}..."
  514. xcnotary notarize "${NOTARIZE_TARGET}" --developer-account "${CODESIGN_IDENT_USER}" --developer-password-keychain-item "OBS-Codesign-Password" --provider "${CODESIGN_IDENT_SHORT}"
  515. fi
  516. }
  517. ## MAIN SCRIPT FUNCTIONS ##
  518. print_usage() {
  519. echo -e "full-build-macos.sh - Build helper script for OBS-Studio\n"
  520. echo -e "Usage: ${0}\n" \
  521. "-d: Skip dependency checks\n" \
  522. "-b: Create macOS app bundle\n" \
  523. "-c: Codesign macOS app bundle\n" \
  524. "-p: Package macOS app into disk image\n" \
  525. "-n: Notarize macOS app and disk image (implies -b)\n" \
  526. "-s: Skip build process (useful for bundling/packaging only)\n" \
  527. "-h: Print this help"
  528. exit 0
  529. }
  530. obs-build-main() {
  531. ensure_dir ${CHECKOUT_DIR}
  532. check_macos_version
  533. step "Fetching OBS tags..."
  534. git fetch origin --tags
  535. GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
  536. GIT_HASH=$(git rev-parse --short HEAD)
  537. GIT_TAG=$(git describe --tags --abbrev=0)
  538. FILE_NAME="obs-studio-${GIT_TAG}-${GIT_HASH}-macOS.dmg"
  539. ##########################################################################
  540. # IMPORTANT:
  541. #
  542. # Be careful when choosing to notarize and code-sign. The script will try
  543. # to sign any pre-existing bundle but also pre-existing images.
  544. #
  545. # This could lead to a package containing a non-signed bundle, which
  546. # will then fail notarization.
  547. #
  548. # To avoid this, run this script with -b -c first, then -p -c or -p -n
  549. # after to make sure that a code-signed bundle will be packaged.
  550. #
  551. ##########################################################################
  552. while getopts ":hdsbnpc" OPTION; do
  553. case ${OPTION} in
  554. h) print_usage ;;
  555. d) SKIP_DEPS=1 ;;
  556. s) SKIP_BUILD=1 ;;
  557. b) BUNDLE_OBS=1 ;;
  558. n) CODESIGN_OBS=1; NOTARIZE_OBS=1 ;;
  559. p) PACKAGE_OBS=1 ;;
  560. c) CODESIGN_OBS=1 ;;
  561. \?) ;;
  562. esac
  563. done
  564. full-build-macos
  565. bundle_macos
  566. codesign_bundle
  567. package_macos
  568. codesign_image
  569. notarize_macos
  570. cleanup
  571. }
  572. obs-build-main $*