diff --git a/.github/scripts/.Aptfile b/.github/scripts/.Aptfile new file mode 100644 index 000000000..40a5f1159 --- /dev/null +++ b/.github/scripts/.Aptfile @@ -0,0 +1,7 @@ +package 'ccache' +package 'cmake' +package 'curl' +package 'git' +package 'jq' +package 'ninja-build', bin: 'ninja' +package 'pkg-config' diff --git a/.github/scripts/.build.zsh b/.github/scripts/.build.zsh index 4ae7beec1..7670c880f 100755 --- a/.github/scripts/.build.zsh +++ b/.github/scripts/.build.zsh @@ -63,6 +63,7 @@ build() { local -r -a _valid_targets=( macos-x86_64 macos-arm64 + linux-x86_64 ) local target local config='RelWithDebInfo' @@ -230,6 +231,33 @@ ${_usage_host:-}" -DOBS_CODESIGN_IDENTITY:STRING=${CODESIGN_IDENT:--} ) ;; + linux-*) + cmake_args+=( + -S ${PWD} -B "build_${target##*-}" + -G "${generator}" + -DCMAKE_BUILD_TYPE:STRING=${config} + -DCEF_ROOT_DIR:PATH="${project_root}/.deps/cef_binary_${CEF_VERSION}_${target//-/_}" + ) + + local cmake_version + read -r _ _ cmake_version <<< "$(cmake --version)" + + cmake_build_args+=("build_${target##*-}" --config ${config}) + + if [[ ${generator} == 'Unix Makefiles' ]] { + cmake_build_args+=(--parallel $(( $(nproc) + 1 ))) + } else { + cmake_build_args+=(--parallel) + } + + cmake_args+=( + -DENABLE_AJA:BOOL=OFF + -DENABLE_WEBRTC:BOOL=OFF + ) + if (( ! UBUNTU_2210_OR_LATER )) cmake_args+=(-DENABLE_NEW_MPEGTS_OUTPUT:BOOL=OFF) + + cmake_install_args+=(build_${target##*-} --prefix ${project_root}/build_${target##*-}/install/${config}) + ;; } if (( _print_config )) { log_output "CMake configuration: ${cmake_args}"; exit 0 } diff --git a/.github/scripts/.package.zsh b/.github/scripts/.package.zsh index 0e5308410..0febfc95c 100755 --- a/.github/scripts/.package.zsh +++ b/.github/scripts/.package.zsh @@ -260,6 +260,36 @@ ${_usage_host:-}" } log_group + + } elif [[ ${host_os} == linux ]] { + local -a cmake_args=() + if (( _loglevel > 1 )) cmake_args+=(--verbose) + + if (( package )) { + log_group "Packaging obs-studio..." + pushd ${project_root} + cmake --build build_${target##*-} --config ${config} -t package ${cmake_args} + output_name="${output_name}-${target##*-}-linux-gnu" + + pushd ${project_root}/build_${target##*-} + local -a files=(obs-studio-*-Linux*.(ddeb|deb)) + for file (${files}) { + mv ${file} ${file//obs-studio-*-Linux/${output_name}} + } + popd + popd + } else { + log_group "Archiving obs-studio..." + output_name="${output_name}-${target##*-}-linux-gnu" + + local _tarflags='cJf' + if (( _loglevel > 1 || ${+CI} )) _tarflags="v${_tarflags}" + + pushd ${project_root}/build_${target##*-}/install/${config} + XZ_OPT=-T0 tar "-${_tarflags}" ${project_root}/build_${target##*-}/${output_name}.tar.xz (bin|lib|share) + popd + } + log_group } } diff --git a/.github/scripts/build-linux b/.github/scripts/build-linux new file mode 120000 index 000000000..43bea7f01 --- /dev/null +++ b/.github/scripts/build-linux @@ -0,0 +1 @@ +./.build.zsh \ No newline at end of file diff --git a/.github/scripts/package-linux b/.github/scripts/package-linux new file mode 120000 index 000000000..aff469015 --- /dev/null +++ b/.github/scripts/package-linux @@ -0,0 +1 @@ +./.package.zsh \ No newline at end of file diff --git a/.github/scripts/utils.zsh/check_linux b/.github/scripts/utils.zsh/check_linux new file mode 100644 index 000000000..d3e74e00f --- /dev/null +++ b/.github/scripts/utils.zsh/check_linux @@ -0,0 +1,61 @@ +autoload -Uz log_info log_status log_error log_debug log_warning log_group + +log_group 'Check Linux build requirements' +log_debug 'Checking Linux distribution name and version...' + +# Check for Ubuntu version 22.10 or later, which have srt and librist available via apt-get +typeset -g -i UBUNTU_2210_OR_LATER=0 +if [[ -f /etc/os_release ]] { + local dist_name + local dist_version + read -r dist_name dist_version <<< "$(source /etc/os_release; print "${NAME} ${VERSION_ID}")" + + autoload -Uz is-at-least + if [[ ${dist_name} == Ubuntu ]] && is-at-least 22.10 ${dist_version}; then + typeset -g -i UBUNTU_2210_OR_LATER=1 + fi +} + +log_debug 'Checking for apt-get...' +if (( ! ${+commands[apt-get]} )) { + log_error 'No apt-get command found. Please install apt' + return 2 +} else { + log_debug "Apt-get located at ${commands[apt-get]}" +} + +local -a dependencies=("${(fA)$(<${SCRIPT_HOME}/.Aptfile)}") +local -a install_list +local binary + +sudo apt-get update -qq + +for dependency (${dependencies}) { + local -a tokens=(${=dependency//(,|:|\')/}) + + if [[ ! ${tokens[1]} == package ]] continue + + if [[ ${#tokens} -gt 2 && ${tokens[3]} == bin ]] { + binary=${tokens[4]} + } else { + binary=${tokens[2]} + } + + if (( ! ${+commands[${binary}]} )) install_list+=(${tokens[2]}) +} + +log_debug "List of dependencies to install: ${install_list}" +if (( #install_list )) { + if (( ! ${+CI} )) log_warning 'Dependency installation via apt may require elevated privileges' + + local -a apt_args=( + ${CI:+-y} + --no-install-recommends + ) + if (( _loglevel == 0 )) apt_args+=(--quiet) + + sudo apt-get ${apt_args} install ${install_list} +} + +rehash +log_group diff --git a/.github/scripts/utils.zsh/setup_linux b/.github/scripts/utils.zsh/setup_linux new file mode 100644 index 000000000..c8f14ab4f --- /dev/null +++ b/.github/scripts/utils.zsh/setup_linux @@ -0,0 +1,187 @@ +autoload -Uz log_group log_error log_status log_info log_debug + +if (( ! ${+commands[curl]} )) { + log_error 'curl not found. Please install curl.' + return 2 +} + +if (( ! ${+commands[jq]} )) { + log_error 'jq not found. Please install jq.' + return 2 +} + +if (( ! ${+project_root} )) { + log_error "'project_root' not set. Please set before running ${0}." + return 2 +} + +if (( ! ${+target} )) { + log_error "'target' not set. Please set before running ${0}." + return 2 +} + +local -a curl_opts=() +if (( ! ${+CI} )) { + curl_opts+=(--progress-bar --continue-at -) +} else { + curl_opts+=(--show-error --silent) +} +curl_opts+=(--location -O ${@}) + +pushd ${project_root} + +typeset -g QT_VERSION +read -r QT_VERSION <<< \ + "$(jq -r --arg target "${target}" \ + '.platformConfig[$target] | { qtVersion } | join(" ")' \ + ${buildspec_file})" + +if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) { + log_group 'Installing obs-studio build dependencies...' + + mkdir -p ${project_root}/.deps + local deps_version + local deps_baseurl + local deps_label + local deps_hash + + IFS=';' read -r deps_version deps_baseurl deps_label deps_hash <<< \ + "$(jq -r --arg target "${target}" \ + '.dependencies["cef"] | {version, baseUrl, "label", "hash": .hashes[$target]} | join(";")' \ + ${buildspec_file})" + + if (( ! deps_version )) { + log_error 'No valid cef spec found in buildspec.json.' + return 2 + } + log_group 'Setting up pre-built Chromium Embedded Framework...' + + pushd ${project_root}/.deps + local _filename="cef_binary_${deps_version}_${target//-/_}.tar.xz" + local _url=${deps_baseurl}/${_filename} + local _target="cef_binary_${deps_version}_${target//-/_}" + typeset -g CEF_VERSION=${deps_version} + + log_status 'Checking for available wrapper library...' + local -i _skip=0 + if [[ -f ${_target}/build/libcef_dll_wrapper/libcef_dll_wrapper.a ]] { + _skip=1 + } + + if ! (( _skip )) { + if [[ ! -f ${_filename} ]] { + log_debug "Running curl ${curl_opts} ${_url}" + curl ${curl_opts} ${_url} && \ + log_status "Downloaded ${deps_label} for ${target}." + } else { + log_status "Found downloaded ${deps_label}" + } + + read -r artifact_checksum _ <<< "$(sha256sum ${_filename})" + if [[ ${deps_hash} != ${artifact_checksum} ]] { + log_error "Checksum of downloaded ${deps_label} does not match specification. + Expected : ${deps_hash} + Actual : ${artifact_checksum}" + return 2 + } + log_status "Checksum of downloaded ${deps_label} matches." + mkdir -p ${_target} && pushd ${_target} + + XZ_OPT=-T0 tar --strip-components 1 -xJf ../${_filename} && log_status "${deps_label} extracted." + + if [[ ! -f build/libcef_dll_wrapper/libcef_dll_wrapper.a ]] { + log_group "Configuring CEF wrapper library..." + + local -a cmake_args=( + -DPROJECT_ARCH:STRING=${target##*-} + -DCEF_COMPILER_FLAGS:STRING="-Wno-deprecated-copy" + -DCMAKE_BUILD_TYPE:STRING=${config} + -DCMAKE_CXX_FLAGS:STRING="-std=c++11 -Wno-deprecated-declarations -Wno-unknonw-warning-option" + -DCMAKE_EXE_LINKER_FLAGS:STRING="-std=c++11" + ) + if (( _loglevel == 0 )) cmake_args+=(-Wno-deprecated -Wno-dev --log-level=ERROR) + if (( ${+commands[ccache]} )) { + cmake_args+=( + -DCMAKE_C_COMPILER_LAUNCHER:STRING=ccache + -DCMAKE_CXX_COMPILER_LAUNCHER:STRING=ccache + ) + } + + cmake -S . -B build -G Ninja ${cmake_args} + + log_group "Building CEF Wrapper library..." + cmake --build build + } + + mkdir -p build/libcef_dll + popd + } else { + log_info 'Found existing Chromium Embedded Framework and loader library...' + } + popd + + local -a apt_args=( + ${CI:+-y} + --no-install-recommends + ) + if (( _loglevel == 0 )) apt_args+=(--quiet) + + local suffix + if [[ ${CPUTYPE} != ${target##*-} ]] { + local -A arch_mappings=( + aarch64 arm64 + x86_64 amd64 + ) + + suffix=":${arch_mappings[${target##*-}]}" + sudo apt-get install ${apt_args} gcc-${${target##*-}//_/-}-linux-gnu g++-${${target##*-}//_/-}-linux-gnu + } + + sudo apt-get install ${apt_args} \ + build-essential \ + libcurl4-openssl-dev \ + libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev \ + libswresample-dev libswscale-dev \ + libjansson-dev \ + libx11-xcb-dev \ + libgles2-mesa-dev libgles2-mesa \ + libwayland-dev \ + libpipewire-0.3-dev \ + libpulse-dev \ + libx264-dev \ + libmbedtls-dev \ + libgl1-mesa-dev \ + libjansson-dev \ + libluajit-5.1-dev python3-dev \ + libx11-dev libxcb-randr0-dev libxcb-shm0-dev libxcb-xinerama0-dev \ + libxcb-composite0-dev libxinerama-dev libxcb1-dev libx11-xcb-dev libxcb-xfixes0-dev \ + swig libcmocka-dev libxss-dev libglvnd-dev \ + libxkbcommon-dev \ + libasound2-dev libfdk-aac-dev libfontconfig-dev libfreetype6-dev libjack-jackd2-dev \ + libpulse-dev libsndio-dev libspeexdsp-dev libudev-dev libv4l-dev libva-dev libvlc-dev \ + libpci-dev libdrm-dev \ + nlohmann-json3-dev libwebsocketpp-dev libasio-dev libvpl-dev libqrcodegencpp-dev + + if (( UBUNTU_2210_OR_LATER )) sudo apt-get install ${apt_args} librist-dev libsrt-openssl-dev + + local -a _qt_packages=() + + if (( QT_VERSION == 6 )) { + _qt_packages+=( + qt6-base-dev + libqt6svg6-dev + qt6-base-private-dev + ) + } else { + log_error "Unsupported Qt version '${QT_VERSION}' specified." + return 2 + } + + sudo apt-get install ${apt_args} ${_qt_packages} +} else { + local cef_version + read -r cef_version <<< \ + "$(jq -r '.dependencies | [.cef.version] | join(" ")' ${buildspec_file})" + + typeset -g CEF_VERSION=${cef_version} +}