diff --git a/.github/scripts/.Brewfile b/.github/scripts/.Brewfile new file mode 100644 index 000000000..cdc51f13e --- /dev/null +++ b/.github/scripts/.Brewfile @@ -0,0 +1,5 @@ +brew "ccache" +brew "cmake" +brew "git" +brew "jq" +brew "xcbeautify" diff --git a/.github/scripts/utils.zsh/check_macos b/.github/scripts/utils.zsh/check_macos new file mode 100644 index 000000000..2e4a5bf3d --- /dev/null +++ b/.github/scripts/utils.zsh/check_macos @@ -0,0 +1,22 @@ +autoload -Uz is-at-least log_group log_info log_error log_status + +local macos_version=$(sw_vers -productVersion) + +log_group 'Install macOS build requirements' +log_info 'Checking macOS version...' +if ! is-at-least 11.0 ${macos_version}; then + log_error "Minimum required macOS version is 11.0, but running on macOS ${macos_version}" + return 2 +else + log_status "macOS ${macos_version} is recent" +fi + +log_info 'Checking for Homebrew...' +if (( ! ${+commands[brew]} )) { + log_error 'No Homebrew command found. Please install Homebrew (https://brew.sh)' + return 2 +} + +brew bundle --file ${SCRIPT_HOME}/.Brewfile +rehash +log_group diff --git a/.github/scripts/utils.zsh/create_diskimage b/.github/scripts/utils.zsh/create_diskimage new file mode 100644 index 000000000..186672127 --- /dev/null +++ b/.github/scripts/utils.zsh/create_diskimage @@ -0,0 +1,62 @@ +autoload -Uz log_debug log_error log_info log_status log_group log_output + +local -r _usage="Usage: %B${0}%b + +Create macOS disk image with contents of " + +if (( ! # )) { + log_error 'Called without arguments.' + log_output ${_usage} + return 2 +} + +local source=${1} +local volume_name=${2} +local output_name=${3} + +log_group "Create macOS disk image" + +local _hdiutil_flags +if (( _loglevel < 1 )) _hdiutil_flags='-quiet' + +trap "hdiutil detach ${_hdiutil_flags} /Volumes/${output_name}; rm temp.dmg; log_group return 2" ERR + +hdiutil create ${_hdiutil_flags} \ + -volname "${volume_name}" \ + -srcfolder ${source} \ + -ov \ + -fs APFS \ + -format UDRW \ + temp.dmg +hdiutil attach ${_hdiutil_flags} \ + -noverify \ + -readwrite \ + -mountpoint /Volumes/${output_name} \ + temp.dmg + +log_info "Waiting 2 seconds to ensure mounted volume is available..." +sleep 2 +log_status "Done" +log_info "Setting up disk volume..." +log_status "Volume icon" +SetFile -c icnC /Volumes/${output_name}/.VolumeIcon.icns +log_status "Icon positions" +osascript package.applescript ${output_name} +log_status "File permissions" +chmod -Rf go-w /Volumes/${output_name} +SetFile -a C /Volumes/${output_name} +rm -rf -- /Volumes/${output_name}/.fseventsd(N) +log_info "Converting disk image..." +hdiutil detach ${_hdiutil_flags} /Volumes/${output_name} +hdiutil convert ${_hdiutil_flags} \ + -format ULMO \ + -ov \ + -o ${output_name}.dmg temp.dmg + +rm temp.dmg + +trap '' ERR + +log_group + +return 0 diff --git a/.github/scripts/utils.zsh/log_debug b/.github/scripts/utils.zsh/log_debug new file mode 100644 index 000000000..9c5843df4 --- /dev/null +++ b/.github/scripts/utils.zsh/log_debug @@ -0,0 +1,3 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 2 )) print -PR -e "${CI:+::debug::}%F{220}DEBUG: ${@}%f" diff --git a/.github/scripts/utils.zsh/log_error b/.github/scripts/utils.zsh/log_error new file mode 100644 index 000000000..f1c7b4364 --- /dev/null +++ b/.github/scripts/utils.zsh/log_error @@ -0,0 +1,3 @@ +local icon=' ✖︎ ' + +print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" diff --git a/.github/scripts/utils.zsh/log_group b/.github/scripts/utils.zsh/log_group new file mode 100644 index 000000000..7b6aca978 --- /dev/null +++ b/.github/scripts/utils.zsh/log_group @@ -0,0 +1,16 @@ +autoload -Uz log_info + +if (( ! ${+_log_group} )) typeset -g _log_group=0 + +if (( ${+CI} )) { + if (( _log_group )) { + print "::endgroup::" + typeset -g _log_group=0 + } + if (( # )) { + print "::group::${@}" + typeset -g _log_group=1 + } +} else { + if (( # )) log_info ${@} +} diff --git a/.github/scripts/utils.zsh/log_info b/.github/scripts/utils.zsh/log_info new file mode 100644 index 000000000..d437c292d --- /dev/null +++ b/.github/scripts/utils.zsh/log_info @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' =>' + + print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" +} diff --git a/.github/scripts/utils.zsh/log_output b/.github/scripts/utils.zsh/log_output new file mode 100644 index 000000000..4d9b52f39 --- /dev/null +++ b/.github/scripts/utils.zsh/log_output @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon='' + + print -PR " ${(r:5:)icon} ${@}" +} diff --git a/.github/scripts/utils.zsh/log_status b/.github/scripts/utils.zsh/log_status new file mode 100644 index 000000000..950e68187 --- /dev/null +++ b/.github/scripts/utils.zsh/log_status @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' >' + + print -PR "%F{2} ${(r:5:)icon}%f ${@}" +} diff --git a/.github/scripts/utils.zsh/log_warning b/.github/scripts/utils.zsh/log_warning new file mode 100644 index 000000000..ceff982a9 --- /dev/null +++ b/.github/scripts/utils.zsh/log_warning @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' =>' + + print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" +} diff --git a/.github/scripts/utils.zsh/mkcd b/.github/scripts/utils.zsh/mkcd new file mode 100644 index 000000000..4f795ce18 --- /dev/null +++ b/.github/scripts/utils.zsh/mkcd @@ -0,0 +1 @@ +[[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1} diff --git a/.github/scripts/utils.zsh/read_codesign b/.github/scripts/utils.zsh/read_codesign new file mode 100644 index 000000000..b0029021e --- /dev/null +++ b/.github/scripts/utils.zsh/read_codesign @@ -0,0 +1,9 @@ +autoload -Uz log_info + +if (( ! ${+CODESIGN_IDENT} )) { + typeset -g CODESIGN_IDENT + log_info 'Setting up Apple Developer ID for application codesigning...' + read CODESIGN_IDENT'?Apple Developer Application ID: ' +} + +typeset -g CODESIGN_TEAM=$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p') diff --git a/.github/scripts/utils.zsh/read_codesign_pass b/.github/scripts/utils.zsh/read_codesign_pass new file mode 100644 index 000000000..f38f26f4d --- /dev/null +++ b/.github/scripts/utils.zsh/read_codesign_pass @@ -0,0 +1,24 @@ +autoload -Uz read_codesign read_codesign_user log_info log_warning + +if (( ! ${+CODESIGN_IDENT} )) { + read_codesign +} + +if (( ! ${+CODESIGN_IDENT_USER} )) { + read_codesign_user +} + +log_info 'Setting up password for notarization keychain...' +if (( ! ${+CODESIGN_IDENT_PASS} )) { + read -s CODESIGN_IDENT_PASS'?Apple Developer ID password: ' +} + +print '' +log_info 'Setting up notarization keychain...' +log_warning " + + Your Apple ID and an app-specific password is necessary for notarization from CLI + + This password will be stored in your macOS keychain under the identifier + 'OBS-Codesign-Password' with access Apple's 'altool' only. + +" +xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id "${CODESIGN_IDENT_USER}" --team-id "${CODESIGN_TEAM}" --password "${CODESIGN_IDENT_PASS}" diff --git a/.github/scripts/utils.zsh/read_codesign_team b/.github/scripts/utils.zsh/read_codesign_team new file mode 100644 index 000000000..5498d9588 --- /dev/null +++ b/.github/scripts/utils.zsh/read_codesign_team @@ -0,0 +1,7 @@ +autoload -Uz log_info + +if (( ! ${+CODESIGN_TEAM} )) { + typeset -g CODESIGN_TEAM + log_info 'Setting up Apple Developer Team ID for codesigning...' + read CODESIGN_TEAM'?Apple Developer Team ID (leave empty to use Apple Developer ID instead): ' +} diff --git a/.github/scripts/utils.zsh/read_codesign_user b/.github/scripts/utils.zsh/read_codesign_user new file mode 100644 index 000000000..c7f016e54 --- /dev/null +++ b/.github/scripts/utils.zsh/read_codesign_user @@ -0,0 +1,7 @@ +autoload -Uz log_info + +if (( ! ${+CODESIGN_IDENT_USER} )) { + typeset -g CODESIGN_IDENT_USER + log_info 'Setting up Apple ID for notarization...' + read CODESIGN_IDENT_USER'?Apple ID: ' +} diff --git a/.github/scripts/utils.zsh/run_xcodebuild b/.github/scripts/utils.zsh/run_xcodebuild new file mode 100644 index 000000000..3f418d4aa --- /dev/null +++ b/.github/scripts/utils.zsh/run_xcodebuild @@ -0,0 +1,7 @@ +if (( _loglevel > 1 )) { + xcodebuild ${@} +} else { + local -a xcbeautify_opts=() + if (( _loglevel == 0 )) xcbeautify_opts+=(--quiet) + xcodebuild ${@} 2>&1 | xcbeautify ${xcbeautify_opts} +} diff --git a/.github/scripts/utils.zsh/set_loglevel b/.github/scripts/utils.zsh/set_loglevel new file mode 100644 index 000000000..15ff2a7dc --- /dev/null +++ b/.github/scripts/utils.zsh/set_loglevel @@ -0,0 +1,17 @@ +autoload -Uz log_debug log_error log_output + +local -r _usage="Usage: %B${0}%b + +Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" + +if (( ! # )); then + log_error 'Called without arguments.' + log_output ${_usage} + return 2 +elif (( ${1} >= 4 )); then + log_error 'Called with loglevel > 3.' + log_output ${_usage} +fi + +typeset -g -i -r _loglevel=${1} +log_debug "Log level set to '${1}'" diff --git a/.github/scripts/utils.zsh/setup_ccache b/.github/scripts/utils.zsh/setup_ccache new file mode 100644 index 000000000..a57f52d64 --- /dev/null +++ b/.github/scripts/utils.zsh/setup_ccache @@ -0,0 +1,42 @@ +autoload -Uz log_debug log_warning log_error + +if (( ! ${+project_root} )) { + log_error "'project_root' not set. Please set before running ${0}." + return 2 +} + +if (( ${+commands[ccache]} )) { + log_debug "Found ccache at ${commands[ccache]}" + + typeset -gx CCACHE_CONFIGPATH="${project_root}/.ccache.conf" + + ccache --set-config=run_second_cpp=true + ccache --set-config=direct_mode=true + ccache --set-config=inode_cache=true + ccache --set-config=compiler_check=content + ccache --set-config=file_clone=true + + local -a sloppiness=( + include_file_mtime + include_file_ctime + file_stat_matches + system_headers + ) + + if [[ ${host_os} == macos ]] { + sloppiness+=( + modules + clang_index_store + ) + + ccache --set-config=sloppiness=${(j:,:)sloppiness} + } + + if (( ${+CI} )) { + ccache --set-config=cache_dir="${GITHUB_WORKSPACE:-${HOME}}/.ccache" + ccache --set-config=max_size="${CCACHE_SIZE:-1G}" + ccache -z > /dev/null + } +} else { + log_warning "No ccache found on the system" +} diff --git a/.gitignore b/.gitignore index b8517d25a..4c30feb4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,108 +1,49 @@ -#binaries -*.exe -*.dll -*.dylib -*.so -*.plugin -*.framework -*.systemextension +# Exclude everything +/* -#cmake -/build*/ -!/build-aux/ -/release*/ -/debug*/ -.vs/ -*.o.d -*.ninja -.ninja* -.dirstamp -/cmake/.CMakeBuildNumber -.deps -CMakeUserPresets.json +# Except for default project files +!/.github +!/build-aux +!/cmake +!/deps +!/docs +!/libobs* +!/plugins +!/tests +!/UI +!.cirrus.xml +!.clang-format +!.cmake-format.json +!.editorconfig +!.git-blame-ignore-devs +!.gitmodules +!.gitignore +!.mailmap +!.swift-format +!AUTHORS +!buildspec.json +!CMakeLists.txt +!CMakePresets.json +!COC.rst +!COMMITMENT +!CONTRIBUTING.rst +!COPYING +!INSTALL +!README.rst -#xcode -*.xcodeproj/ -/xcodebuild/ +# Exclude lock files +*.lock.json -#clion -.idea/ -cmake-build-debug/ - -#other stuff (windows stuff, qt moc stuff, etc) -Release_MD/ -Release/ -Debug/ -x64/ -ipch/ -GeneratedFiles/ -.moc/ -/UI/obs.rc -.vscode/ -/CI/include/*.lock.json -install_temp/ - -/other/ - -#make stuff -configure -depcomp -install-sh -Makefile.in -Makefile - -#python -__pycache__ - -#sphinx +# Exclude files generated by Sphinx in-tree /docs/sphinx/_build/* !/docs/sphinx/_build/.gitignore !/docs/sphinx/Makefile -#random useless file stuff -*.dmg -*.app -.directory -.hg -.depend -tags -*.trace -*.vsp -*.psess -*.swp -*.dat -*.clbin -*.log -*.tlog -*.sdf -*.opensdf -*.xml -*.ipch -*.css -*.xslt -*.aps -*.suo -*.ncb -*.user -*.lo -*.ilk -*.la -*.o -*.obj -*.pdb -*.res -*.dep -*.zip -*.lnk -*.chm -*~ -.DS_Store -*/.DS_Store -*/**/.DS_Store +# Exclude modified Flatpak files +build-aux/flatpak-github-action-modified-* -#flatpak -/.flatpak-builder/ -/_flatpak_build/ -/flatpak_app/ -/repo/ -/CI/flatpak/flatpak-github-action-modified-* +# Exclude macOS legacy resource forks +.DS_Store + +# Exclude CMake build number cache +/cmake/.CMakeBuildNumber diff --git a/build-aux/.functions/log_debug b/build-aux/.functions/log_debug new file mode 100644 index 000000000..6a477a181 --- /dev/null +++ b/build-aux/.functions/log_debug @@ -0,0 +1,3 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f" diff --git a/build-aux/.functions/log_error b/build-aux/.functions/log_error new file mode 100644 index 000000000..f1c7b4364 --- /dev/null +++ b/build-aux/.functions/log_error @@ -0,0 +1,3 @@ +local icon=' ✖︎ ' + +print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" diff --git a/build-aux/.functions/log_group b/build-aux/.functions/log_group new file mode 100644 index 000000000..7b6aca978 --- /dev/null +++ b/build-aux/.functions/log_group @@ -0,0 +1,16 @@ +autoload -Uz log_info + +if (( ! ${+_log_group} )) typeset -g _log_group=0 + +if (( ${+CI} )) { + if (( _log_group )) { + print "::endgroup::" + typeset -g _log_group=0 + } + if (( # )) { + print "::group::${@}" + typeset -g _log_group=1 + } +} else { + if (( # )) log_info ${@} +} diff --git a/build-aux/.functions/log_info b/build-aux/.functions/log_info new file mode 100644 index 000000000..d437c292d --- /dev/null +++ b/build-aux/.functions/log_info @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' =>' + + print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" +} diff --git a/build-aux/.functions/log_output b/build-aux/.functions/log_output new file mode 100644 index 000000000..4d9b52f39 --- /dev/null +++ b/build-aux/.functions/log_output @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon='' + + print -PR " ${(r:5:)icon} ${@}" +} diff --git a/build-aux/.functions/log_status b/build-aux/.functions/log_status new file mode 100644 index 000000000..950e68187 --- /dev/null +++ b/build-aux/.functions/log_status @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' >' + + print -PR "%F{2} ${(r:5:)icon}%f ${@}" +} diff --git a/build-aux/.functions/log_warning b/build-aux/.functions/log_warning new file mode 100644 index 000000000..ceff982a9 --- /dev/null +++ b/build-aux/.functions/log_warning @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' =>' + + print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" +} diff --git a/build-aux/.functions/set_loglevel b/build-aux/.functions/set_loglevel new file mode 100644 index 000000000..e32f4bb35 --- /dev/null +++ b/build-aux/.functions/set_loglevel @@ -0,0 +1,17 @@ +autoload -Uz log_debug log_error + +local -r _usage="Usage: %B${0}%b + +Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" + +if (( ! # )); then + log_error 'Called without arguments.' + log_output ${_usage} + return 2 +elif (( ${1} >= 4 )); then + log_error 'Called with loglevel > 3.' + log_output ${_usage} +fi + +typeset -g -i -r _loglevel=${1} +log_debug "Log level set to '${1}'" diff --git a/build-aux/.run-format.zsh b/build-aux/.run-format.zsh new file mode 100755 index 000000000..4f56bcbb1 --- /dev/null +++ b/build-aux/.run-format.zsh @@ -0,0 +1,190 @@ +#!/usr/bin/env zsh + +builtin emulate -L zsh +setopt EXTENDED_GLOB +setopt PUSHD_SILENT +setopt ERR_EXIT +setopt ERR_RETURN +setopt NO_UNSET +setopt PIPE_FAIL +setopt NO_AUTO_PUSHD +setopt NO_PUSHD_IGNORE_DUPS +setopt FUNCTION_ARGZERO + +## Enable for script debugging +# setopt WARN_CREATE_GLOBAL +# setopt WARN_NESTED_VAR +# setopt XTRACE + +autoload -Uz is-at-least && if ! is-at-least 5.2; then + print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade zsh to fix this issue." + exit 1 +fi + +invoke_formatter() { + if (( # < 1 )) { + log_error "Usage invoke_formatter [formatter_name]" + exit 2 + } + + case ${1} { + clang) + if (( ${+commands[clang-format-13]} )) { + local formatter=clang-format-13 + } elif (( ${+commands[clang-format]} )) { + local formatter=clang-format + } else { + log_error "No viable clang-format version found (required 13.0.1)" + exit 2 + } + + local -a formatter_version=($(${formatter} --version)) + + if ! is-at-least 13.0.1 ${formatter_version[-1]}; then + log_error "clang-format is not version 13.0.1 or above (found ${formatter_version[-1]}." + exit 2 + fi + + if ! is-at-least ${formatter_version[-1]} 13.0.1; then + log_error "clang-format is more recent than version 13.0.1 (found ${formatter_version[-1]})." + exit 2 + fi + + local -a source_files=((libobs|libobs-*|UI|plugins)/**/*.(c|cpp|h|hpp|m|mm)(.N)) + source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|enc-amf|mac-syphon/syphon-framework|obs-outputs/ftl-sdk)/*}) + + local -a format_args=(-style=file -fallback-style=none) + if (( _loglevel > 2 )) format_args+=(--verbose) + ;; + cmake) + local formatter=cmake-format + if (( ${+commands[cmake-format]} )) { + local cmake_format_version=$(cmake-format --version) + + if ! is-at-least 0.6.13 ${cmake_format_version}; then + log_error "cmake-format is not version 0.6.13 or above (found ${cmake_format_version})." + exit 2 + fi + } else { + log_error "No viable cmake-format version found (required 0.6.13)" + exit 2 + } + + local -a source_files=((libobs|libobs-*|UI|plugins|cmake)/**/(CMakeLists.txt|*.cmake)(.N)) + source_files=(${source_files:#*/(obs-outputs/ftl-sdk|jansson|decklink/*/decklink-sdk|enc-amf|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) + + local -a format_args=() + if (( _loglevel > 2 )) format_args+=(--log-level debug) + ;; + swift) + local formatter=swift-format + if (( ${+commands[swift-format]} )) { + local swift_format_version=$(swift-format --version) + + if ! is-at-least 508.0.0 ${swift_format_version}; then + log_error "swift-format is not version 508.0.0 or above (found ${swift_format_version})." + exit 2 + fi + } else { + log_error "No viable swift-format version found (required 508.0.0)" + exit 2 + } + + local -a source_files=((libobs|libobs-*|UI|plugins)/**/*.swift(.N)) + + local -a format_args=() + ;; + *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, cmake-format, and swift-format."; exit 2 ;; + } + + local file + local -i num_failures=0 + if (( check_only )) { + for file (${source_files}) { + if (( _loglevel > 1 )) log_info "Checking format of ${file}..." + + if ! "${formatter}" ${format_args} "${file}" | diff -q "${file}" - &> /dev/null; then + log_error "${file} requires formatting changes." + + if (( fail_on_error == 2 )) return 2; + num_failures=$(( num_failures + 1 )) + else + if (( _loglevel > 1 )) log_status "${file} requires no formatting changes." + fi + } + if (( fail_on_error && num_failures )) return 2; + } elif (( ${#source_files} )) { + format_args+=(-i) + "${formatter}" ${format_args} ${source_files} + } +} + +run_format() { + if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h} + if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]} + + typeset -g host_os=${${(L)$(uname -s)}//darwin/macos} + local -i fail_on_error=0 + local -i check_only=0 + local -i verbosity=1 + local -r _version='1.0.0' + + fpath=("${SCRIPT_HOME}/.functions" ${fpath}) + autoload -Uz set_loglevel log_info log_error log_output log_status log_warning + + local -r _usage=" +Usage: %B${functrace[1]%:*}%b