From 56d07f44be8cda3fc1f183340b838a07638f81cd Mon Sep 17 00:00:00 2001 From: Levon Gevorgyan Date: Sun, 20 Apr 2025 09:43:08 -0500 Subject: [PATCH] initial working ffmpeg static/shared --- .dockerignore | 1 + .gitignore | 1 + .vscode/settings.json | 3 + cfgs/builds.json | 35 ++++ cfgs/compile_opts.json | 13 ++ main.sh | 57 ++++++ run-container.sh | 14 ++ scripts/build-docker-images.sh | 24 +++ scripts/build.sh | 324 +++++++++++++++++++++++++++++++++ scripts/common.sh | 35 ++++ scripts/install_deps.sh | 192 +++++++++++++++++++ 11 files changed, 699 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 cfgs/builds.json create mode 100644 cfgs/compile_opts.json create mode 100755 main.sh create mode 100755 run-container.sh create mode 100755 scripts/build-docker-images.sh create mode 100644 scripts/build.sh create mode 100644 scripts/common.sh create mode 100644 scripts/install_deps.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..79bd19b --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +gitignore* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..79bd19b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +gitignore* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3b66410 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/cfgs/builds.json b/cfgs/builds.json new file mode 100644 index 0000000..b507cfe --- /dev/null +++ b/cfgs/builds.json @@ -0,0 +1,35 @@ +{ + "ffmpeg": { + "ver": "7cd1edeaa410d977a9f1ff8436f480cb45b80178", + "ext": "git", + "url": "https://github.com/FFmpeg/FFmpeg/" + }, + "hdr10plus_tool": { + "ver": "1.7.0", + "ext": "tar.gz", + "url": "https://github.com/quietvoid/hdr10plus_tool/archive/refs/tags/${ver}.${ext}" + }, + "dovi_tool": { + "ver": "2.2.0", + "ext": "tar.gz", + "url": "https://github.com/quietvoid/dovi_tool/archive/refs/tags/${ver}.${ext}" + }, + "libsvtav1_psy": { + "ver": "3.0.2", + "ext": "tar.gz", + "url": "https://github.com/psy-ex/svt-av1-psy/archive/refs/tags/v${ver}.${ext}", + "deps": [ + "dovi_tool", "hdr10plus_tool" + ] + }, + "libopus": { + "ver": "1.5.2", + "ext": "tar.gz", + "url": "https://github.com/xiph/opus/releases/download/v${ver}/opus-${ver}.${ext}" + }, + "libdav1d": { + "ver": "1.5.0", + "ext": "tar.xz", + "url": "http://downloads.videolan.org/videolan/dav1d/${ver}/dav1d-${ver}.${ext}" + } +} diff --git a/cfgs/compile_opts.json b/cfgs/compile_opts.json new file mode 100644 index 0000000..15b579d --- /dev/null +++ b/cfgs/compile_opts.json @@ -0,0 +1,13 @@ +{ + "clean": true, + "lto": false, + "optimization": 0, + "static": true, + "shared": false, + "cpu": "native", + "arch": "native", + "target_windows": false, + "ffmpeg_enable": [ + "libopus", "libdav1d", "libsvtav1_psy" + ] +} \ No newline at end of file diff --git a/main.sh b/main.sh new file mode 100755 index 0000000..0b5bed8 --- /dev/null +++ b/main.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +REPO_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" +IGN_DIR="${REPO_DIR}/gitignore" +DL_DIR="${IGN_DIR}/downloads" +BUILD_DIR="${IGN_DIR}/builds" +PREFIX="${IGN_DIR}/${OS}_sysroot" +CCACHE_DIR="${IGN_DIR}/ccache" +export REPO_DIR IGN_DIR DL_DIR PREFIX BUILD_DIR CCACHE_DIR + +# function names, descriptions, completions +FB_FUNC_NAMES=() +declare -A FB_FUNC_DESCS +declare -A FB_FUNC_COMPLETION + +BUILD_CFG="${REPO_DIR}/"cfgs/builds.json +COMPILE_CFG="${REPO_DIR}/"cfgs/compile_opts.json +mapfile -t BUILDS < <(jq -r 'keys[]' "$BUILD_CFG") +export BUILD_CFG COMPILE_CFG BUILDS + +# enable what ffmpeg builds +unset FFMPEG_ENABLES +export FFMPEG_ENABLES +for enable in $(jq -r '.ffmpeg_enable[]' "$COMPILE_CFG"); do + FFMPEG_ENABLES+=("${enable}") +done + +src_scripts() { + for script in "${REPO_DIR}/scripts/"*.sh; do + # shellcheck disable=SC1090 + source "${script}" + done +} + +FB_FUNC_NAMES+=('print_cmds') +FB_FUNC_DESCS['print_cmds']='print usable commands' +print_cmds() { + echo -e "\n~~~ Usable Commands ~~~" + for funcname in "${FB_FUNC_NAMES[@]}"; do + echo -e "${CYAN}${funcname}${NC}:\n\t" "${FB_FUNC_DESCS[${funcname}]}" + done + echo -e "~~~~~~~~~~~~~~~~~~~~~~~\n" +} + +set_completions() { + for funcname in "${FB_FUNC_NAMES[@]}"; do + complete -W "${FB_FUNC_COMPLETION[${funcname}]}" "${funcname}" + done +} + +# shellcheck disable=SC1091 +source "${HOME}/.bashrc" +src_scripts || return 1 +determine_os || return 1 +set_compile_opts || return 1 +print_cmds || return 1 +set_completions || return 1 diff --git a/run-container.sh b/run-container.sh new file mode 100755 index 0000000..8cdc082 --- /dev/null +++ b/run-container.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +THIS_DIR="$(dirname "${BASH_SOURCE[0]}")" +IMAGE_NAME='ffmpeg_builder' +DISTROS=( debian ubuntu archlinux fedora ) +DISTROS=( debian ) + +for DISTRO in "${DISTROS[@]}"; do + docker run \ + --rm -it \ + -v "${THIS_DIR}":/workdir \ + -w /workdir \ + "${IMAGE_NAME}-${DISTRO}" bash main.sh +done \ No newline at end of file diff --git a/scripts/build-docker-images.sh b/scripts/build-docker-images.sh new file mode 100755 index 0000000..dcdc35a --- /dev/null +++ b/scripts/build-docker-images.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +FB_FUNC_NAMES+=('build_docker_images') +FB_FUNC_DESCS['build_docker_images']='build docker images with required dependencies pre-installed' +build_docker_images() { + DISTROS=( debian ubuntu archlinux fedora ) + DOCKERFILE_DIR="${IGN_DIR}/Dockerfiles" + test -d "${DOCKERFILE_DIR}" || mkdir -p "${DOCKERFILE_DIR}" + for distro in "${DISTROS[@]}"; do + echo "\ +FROM ${distro} +COPY scripts/ /ffmpeg-builder/scripts/ +COPY main.sh /ffmpeg-builder/ +RUN bash -c 'source /ffmpeg-builder/main.sh ; install_deps' || exit 1" \ + > "${DOCKERFILE_DIR}/Dockerfile_${distro}" + image_tag="ffmpeg_builder_${distro}" + dockerfile="Dockerfile_${distro}" + echo_info "building ${image_tag}" + docker build \ + -t "${image_tag}" \ + -f "${DOCKERFILE_DIR}/${dockerfile}" \ + "${REPO_DIR}/" + done +} \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..3314f24 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,324 @@ +#!/usr/bin/env bash + +set_compile_opts() { + unset CLEAN OPT_LVL LDFLAGS \ + C_FLAGS CXX_FLAGS CPP_FLAGS \ + CONFIGURE_FLAGS MESON_FLAGS \ + RUSTFLAGS CMAKE_FLAGS \ + FFMPEG_EXTRA_FLAGS + export CLEAN OPT_LVL LDFLAGS \ + C_FLAGS CXX_FLAGS CPP_FLAGS \ + CONFIGURE_FLAGS MESON_FLAGS \ + RUSTFLAGS CMAKE_FLAGS \ + FFMPEG_EXTRA_FLAGS + + # set job count for all builds + JOBS="$(nproc)" + export JOBS + + machine="$(cc -dumpmachine)" + test "${machine}" != '' || return 1 + + # set prefix flags + CONFIGURE_FLAGS+=("--prefix=${PREFIX}") + MESON_FLAGS+=("--prefix" "${PREFIX}") + CMAKE_FLAGS+=("-DCMAKE_PREFIX_PATH=${PREFIX}") + CMAKE_FLAGS+=("-DCMAKE_INSTALL_PREFIX=${PREFIX}") + export PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH}" + export PKG_CONFIG_PATH="${PREFIX}/lib/${machine}/pkgconfig:${PKG_CONFIG_PATH}" + echo_info "PKG_CONFIG_PATH = ${PKG_CONFIG_PATH}" + + # add prefix include + C_FLAGS+="-I${PREFIX}/include " + + # enabling a clean build + if test "$(jq .clean "${COMPILE_CFG}")" == 'true'; then + CLEAN='make clean ;' + echo_info "performing clean build" + else + CLEAN='' + fi + + # enabling link-time optimization + # shellcheck disable=SC2034 + unset LTO_SWITCH LTO_FLAG LTO_BOOL + export LTO_SWITCH LTO_FLAG LTO_BOOL + if test "$(jq .lto "${COMPILE_CFG}")" == 'true'; then + echo_info "building with LTO" + LTO_SWITCH='ON' + LTO_FLAG='-flto' + LTO_BOOL='true' + CONFIGURE_FLAGS+=('--enable-lto') + else + echo_info "building without LTO" + LTO_SWITCH='OFF' + LTO_FLAG='' + LTO_BOOL='false' + fi + + # setting optimization level + OPT_LVL="$(jq .optimization "${COMPILE_CFG}")" + if test "${OPT_LVL}" == ''; then + OPT_LVL='0' + fi + + MESON_FLAGS+=("--optimization=${OPT_LVL}") + echo_info "building with optimization: ${OPT_LVL}" + + # static/shared linking + unset PKG_CFG_FLAGS + export PKG_CFG_FLAGS + if test "$(jq .static "${COMPILE_CFG}")" == 'true'; then + LDFLAGS+='-static' + CONFIGURE_FLAGS+=('--enable-static') + CMAKE_FLAGS+=("-DBUILD_SHARED_LIBS=OFF") + MESON_FLAGS+=('--default-library=static') + PKG_CFG_FLAGS='--static' + fi + if test "$(jq .shared "${COMPILE_CFG}")" == 'true'; then + CONFIGURE_FLAGS+=('--enable-shared') + CMAKE_FLAGS+=("-DBUILD_SHARED_LIBS=ON") + CMAKE_FLAGS+=("-DCMAKE_INSTALL_RPATH=${PREFIX}/lib;${PREFIX}/lib/${machine}") + FFMPEG_EXTRA_FLAGS+=('--enable-rpath') + fi + + # architecture/cpu compile flags + export CPU ARCH + CPU="$(jq -r .cpu "${COMPILE_CFG}")" + ARCH="$(jq -r .arch "${COMPILE_CFG}")" + # arm prefers -mcpu over -march + # https://community.arm.com/arm-community-blogs/b/tools-software-ides-blog/posts/compiler-flags-across-architectures-march-mtune-and-mcpu + arch_flags="" + test_arch="$(uname -m)" + if [[ "${test_arch}" == "x86_64" ]]; then + arch_flags="-march=${CPU}" + elif [[ "${test_arch}" == "aarch64" || \ + "${test_arch}" == "arm64" ]] + then + arch_flags="-mcpu=${CPU}" + fi + + C_FLAGS+="-O${OPT_LVL} ${LTO_FLAG} ${arch_flags}" + CXX_FLAGS="${C_FLAGS}" + CPP_FLAGS="${C_FLAGS}" + RUSTFLAGS="-C target-cpu=${CPU}" + CMAKE_FLAGS+=("-DCMAKE_C_FLAGS=${C_FLAGS}") + CMAKE_FLAGS+=("-DCMAKE_CXX_FLAGS=${CXX_FLAGS}") + echo_info "CONFIGURE_FLAGS =" "${CONFIGURE_FLAGS[@]}" + echo_info "C_FLAGS = ${C_FLAGS}" + echo_info "RUSTFLAGS = ${RUSTFLAGS}" + echo_info "CMAKE_FLAGS =" "${CMAKE_FLAGS[@]}" + echo_info "PKG_CFG_FLAGS = ${PKG_CFG_FLAGS}" + + # extra ffmpeg flags + if [[ "${TARGET_WINDOWS}" == '1' ]]; then + FFMPEG_EXTRA_FLAGS+=( + '--cross-prefix=x86_64-w64-mingw32-' + '--target-os=mingw32' + '--cc=x86_64-w64-mingw32-gcc' + '--cxx=x86_64-w64-mingw32-g++' + '--ar=x86_64-w64-mingw32-gcc-ar' + '--ranlib=x86_64-w64-mingw32-gcc-ranlib' + '--nm=x86_64-w64-mingw32-gcc-nm' + ) + fi + echo_info "FFMPEG_EXTRA_FLAGS =" "${FFMPEG_EXTRA_FLAGS[@]}" + + echo +} +# set_compile_opts || return 1 + +get_json_conf() { + local build="${1}" + # make sure there is a build config for the enabled build + test "$(jq -r ".${build}" "$BUILD_CFG")" == 'null' && return 1 + + unset ver ext url deps extracted_dir + export ver ext url deps extracted_dir + ver="$(jq -r ".${build}.ver" "$BUILD_CFG")" + ext="$(jq -r ".${build}.ext" "$BUILD_CFG")" + eval "url=\"$(jq -r ".${build}.url" "$BUILD_CFG")\"" + jq -r ".${build}.deps[]" "$BUILD_CFG" >/dev/null 2>/dev/null && \ + mapfile -t deps < <(jq -r ".${build}.deps[]" "$BUILD_CFG") + jq -r ".${build}.deps[]" "$BUILD_CFG" >/dev/null 2>/dev/null && \ + mapfile -t deps < <(jq -r ".${build}.deps[]" "$BUILD_CFG") + extracted_dir="${BUILD_DIR}/${build}-v${ver}" +} + +download_release() { + local build="${1}" + # set env for wget download + get_json_conf "${build}" || return 1 + local base_path="${build}-v${ver}" + local base_dl_path="${DL_DIR}/${base_path}" + + # remove other versions of a download + for wrong_ver_dl in "${DL_DIR}/${build}"*; do + if [[ "${wrong_ver_dl}" =~ ${base_path} ]]; then + continue + fi + echo_warn "removing wrong version: ${wrong_ver_dl}" + rm -rf "${wrong_ver_dl}" + done + # remove other versions of a build + for wrong_ver_build in "${BUILD_DIR}/${build}"*; do + if [[ "${wrong_ver_build}" =~ ${base_path} ]]; then + continue + fi + echo_warn "removing wrong version: ${extracted_dir}" + rm -rf "${wrong_ver_build}" + done + + if test "${ext}" != "git"; then + wget_out="${base_dl_path}.${ext}" + + # download archive if not present + if ! test -f "${wget_out}"; then + echo_info "downloading ${build}" + echo_if_fail wget "${url}" -O "${wget_out}" + fi + + # create new build directory + test -d "${extracted_dir}" || \ + { + mkdir "${extracted_dir}" + tar -xf "${wget_out}" --strip-components=1 -C "${extracted_dir}" + } + else + # for git downloads + test -d "${base_dl_path}" || { + git clone "${url}" "${base_dl_path}" || return 1 + cp -r "${base_dl_path}" "${extracted_dir}" || return 1 + } + fi +} + +FB_FUNC_NAMES+=('do_build') +# shellcheck disable=SC2034 +FB_FUNC_DESCS['do_build']='build a specific project' +# shellcheck disable=SC2034 +FB_FUNC_COMPLETION['do_build']="${BUILDS[*]}" +do_build() { + local build="${1}" + download_release "${build}" || return 1 + get_json_conf "${build}" || return 1 + for dep in "${deps[@]}"; do + do_build "${dep}" + done + get_json_conf "${build}" || return 1 + echo_info "building ${build}" + pushd "$extracted_dir" >/dev/null || return 1 + echo_if_fail build_"${build}" + retval=$? + popd >/dev/null || return 1 + test ${retval} -eq 0 || return ${retval} + echo_pass "built ${build}" +} + +FB_FUNC_NAMES+=('build') +# shellcheck disable=SC2034 +FB_FUNC_DESCS['build']='build ffmpeg with desired configuration' +build() { + # set_compile_opts || return 1 + + test -d "${DL_DIR}" || mkdir -p "${DL_DIR}" + test -d "${CCACHE_DIR}" || mkdir -p "${CCACHE_DIR}" + test -d "${BUILD_DIR}" || mkdir -p "${BUILD_DIR}" + rm -rf "${PREFIX:?}" && mkdir -p "${PREFIX}" + + for build in "${FFMPEG_ENABLES[@]}"; do + do_build "${build}" || return 1 + done + do_build "ffmpeg" || return 1 + + return 0 +} + +build_hdr10plus_tool() { + test "${CLEAN}" != '' && cargo clean + ccache cargo build --release + test -d "${PREFIX}/bin/" || mkdir "${PREFIX}/bin/" + cp target/release/hdr10plus_tool "${PREFIX}/bin/" || return 1 + + # build libhdr10plus + cd hdr10plus || return 1 + ccache cargo cbuild --release + cargo cinstall --prefix="${PREFIX}" --release || return 1 +} + +build_dovi_tool() { + test "${CLEAN}" != '' && cargo clean + ccache cargo build --release + test -d "${PREFIX}/bin/" || mkdir "${PREFIX}/bin/" + cp target/release/dovi_tool "${PREFIX}/bin/" || return 1 + + # build libdovi + cd dolby_vision || return 1 + ccache cargo cbuild --release + cargo cinstall --prefix="${PREFIX}" --release || return 1 +} + +build_libsvtav1_psy() { + ${CLEAN} + cmake \ + "${CMAKE_FLAGS[@]}" \ + -DSVT_AV1_LTO="${LTO_SWITCH}" \ + -DENABLE_AVX512=ON \ + -DBUILD_TESTING=OFF \ + -DCOVERAGE=OFF \ + -DLIBDOVI_FOUND=1 \ + -DLIBHDR10PLUS_RS_FOUND=1 || return 1 + ccache make -j"${JOBS}" || return 1 + make -j"${JOBS}" install || return 1 +} + +build_libopus() { + $CLEAN + ./configure \ + "${CONFIGURE_FLAGS[@]}" || return 1 + ccache make -j"${JOBS}" || return 1 + make -j"${JOBS}" install || return 1 + return 0 +} + +build_libdav1d() { + test "${CLEAN}" != '' && { + rm -rf build.user + mkdir build.user + } + meson \ + setup . build.user \ + "${MESON_FLAGS[@]}" \ + -Dc_args="${C_FLAGS}" \ + -Dcpp_args="${CPP_FLAGS}" \ + -Db_lto="${LTO_BOOL}" || return 1 + ccache ninja -vC build.user || return 1 + ninja -vC build.user install || return 1 +} + +build_ffmpeg() { + for enable in "${FFMPEG_ENABLES[@]}"; do + test "${enable}" == 'libsvtav1_psy' && enable='libsvtav1' + CONFIGURE_FLAGS+=("--enable-${enable}") + done + $CLEAN + ./configure \ + "${CONFIGURE_FLAGS[@]}" \ + "${FFMPEG_EXTRA_FLAGS[@]}" \ + --pkg-config='pkg-config' \ + --pkg-config-flags="${PKG_CFG_FLAGS}" \ + --disable-debug --enable-nonfree \ + --enable-gpl --enable-version3 \ + --cpu="${CPU}" --arch="${ARCH}" \ + --extra-cflags="${C_FLAGS}" \ + --extra-cxxflags="${CXX_FLAGS}" \ + --extra-ldflags="${LDFLAGS}" \ + --disable-doc \ + --disable-htmlpages \ + --disable-podpages \ + --disable-txtpages \ + --disable-autodetect || return 1 + ccache make -j"${JOBS}" || return 1 + make -j"${JOBS}" install || return 1 + return 0 +} \ No newline at end of file diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100644 index 0000000..c42b87e --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 + +# ANSI colors +RED='\e[0;31m' +CYAN='\e[0;36m' +GREEN='\e[0;32m' +YELLOW='\e[0;33m' +NC='\e[0m' + +# echo wrappers +echo_fail() { echo -e "${RED}FAIL${NC}:" "$@" ; } +echo_info() { echo -e "${CYAN}INFO${NC}:" "$@" ; } +echo_pass() { echo -e "${GREEN}PASS${NC}:" "$@" ; } +echo_warn() { echo -e "${YELLOW}WARN${NC}:" "$@" ; } +echo_exit() { echo_fail "$@" ; exit 1 ; } + +echo_if_fail() { + local cmd=("$@") + local out="/tmp/.stdout-${RANDOM}" + local err="/tmp/.stderr-${RANDOM}" + "${cmd[@]}" > ${out} 2> ${err} + local retval=$? + if ! test ${retval} -eq 0; then + echo + echo_fail "command [" "${cmd[@]}" "] failed" + echo_warn "command output:" + tail -n 10 ${out} + tail -n 10 ${err} + echo + fi + rm ${out} ${err} + return ${retval} +} diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh new file mode 100644 index 0000000..66354da --- /dev/null +++ b/scripts/install_deps.sh @@ -0,0 +1,192 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2317 +# shellcheck disable=SC2034 + +determine_darwin_pkg_mgr() { + if ! command -v brew >/dev/null; then + echo_warning "brew not found" + echo_info "install brew:" + # shellcheck disable=SC2016 + echo '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' + return 1 + fi + PKG_MGR=brew + check_pkg_exists() { + "${PKG_MGR}" list "${1}" + } + PKG_MGR_UPD="${PKG_MGR} update" + PKG_MGR_INST="${PKG_MGR} install" +} + +determine_linux_debian_pkg_mgr() { + if command -v apt-get >/dev/null; then + PKG_MGR=apt-get + else + echo_fail "no package manager found" + return 1 + fi + check_pkg_exists() { + dpkg -l "${1}" >/dev/null 2>/dev/null + } + export DEBIAN_FRONTEND=noninteractive + PKG_MGR_UPD="${SUDO} ${PKG_MGR} update" + PKG_MGR_INST="${SUDO} ${PKG_MGR} install -y" +} + +determine_linux_arch_pkg_mgr() { + if command -v pacman >/dev/null; then + PKG_MGR=pacman + else + echo_fail "no package manager found" + return 1 + fi + check_pkg_exists() { + local pkg_check="${1}" + "${PKG_MGR}" -Qi "${pkg_check}" >/dev/null 2>/dev/null + } + PKG_MGR_UPD="${SUDO} ${PKG_MGR} -Syy" + PKG_MGR_INST="${SUDO} ${PKG_MGR} -S --noconfirm --needed" +} + +determine_linux_fedora_pkg_mgr() { + if command -v dnf >/dev/null; then + PKG_MGR=dnf + else + echo_fail "no package manager found" + return 1 + fi + check_pkg_exists() { + local pkg_check="${1}" + test "${pkg_check}" == 'wget' && pkg_check=wget2 + "${PKG_MGR}" list -q --installed "${pkg_check}" >/dev/null 2>/dev/null + } + PKG_MGR_UPD="${SUDO} ${PKG_MGR} check-update" + PKG_MGR_INST="${SUDO} ${PKG_MGR} install -y" +} + +determine_os() { + unset OS + local UNAME="$(uname)" + if test "${UNAME}" == 'Linux'; then + # shellcheck disable=SC1091 + source /etc/os-release + OS="${UNAME}-${ID_LIKE}" + elif test "${UNAME}" == 'Darwin'; then + OS="${UNAME}" + else + echo_exit "Unable to determine OS for ${UNAME}" + fi + export OS + OS="${OS,,}" + OS="${OS//-/_}" + + if test "$(jq -r ".target_windows" "$COMPILE_CFG")" == 'true'; then + echo_info "targeting windows" + export TARGET_WINDOWS=1 + OS+="_windows" + fi + + return 0 +} + +determine_pkg_mgr() { + unset PKG_MGR PKG_MGR_UPD PKG_MGR_INST + determine_"${OS}"_pkg_mgr + return 0 +} + +check_for_req_pkgs() { + echo_info "OS=${OS}" + echo_info "checking for required packages" + local common_pkgs=( + autoconf automake cmake libtool + texinfo wget nasm yasm python3 + meson doxygen jq ccache gawk + ) + # shellcheck disable=SC2034 + local brew_pkgs=( + "${common_pkgs[@]}" pkgconf mkvtoolnix + ) + local common_linux_pkgs=( + "${common_pkgs[@]}" clang valgrind + curl bc lshw xxd pkgconf + ) + # shellcheck disable=SC2034 + local apt_get_pkgs=( + "${common_linux_pkgs[@]}" build-essential + git-core libass-dev libfreetype6-dev + libsdl2-dev libva-dev libvdpau-dev + libvorbis-dev libxcb1-dev pipx + libxcb-shm0-dev libxcb-xfixes0-dev + zlib1g-dev libssl-dev ninja-build + gobjc++ mawk libnuma-dev + mediainfo mkvtoolnix libgtest-dev + ) + # shellcheck disable=SC2034 + local pacman_pkgs=( + "${common_linux_pkgs[@]}" base-devel + python-pipx ninja + ) + # shellcheck disable=SC2034 + local dnf_pkgs=( + "${common_linux_pkgs[@]}" openssl-devel + pipx ninja-build + ) + + if test "${TARGET_WINDOWS}" -eq 1; then + apt_get_pkgs+=(gcc-mingw-w64 g++-mingw-w64) + fi + + local req_pkgs_env_name="${PKG_MGR/-/_}_pkgs" + declare -n req_pkgs="${req_pkgs_env_name}" + local missing_pkgs=() + for pkg in "${req_pkgs[@]}"; do + check_pkg_exists "${pkg}" || missing_pkgs+=("${pkg}") + done + if ! test "${#missing_pkgs}" -eq 0; then + echo_warn "missing packages:" "${missing_pkgs[@]}" + # shellcheck disable=SC2086 + ${PKG_MGR_UPD} + # shellcheck disable=SC2086 + ${PKG_MGR_INST} "${missing_pkgs[@]}" || return 1 + fi + echo_pass "packages from ${PKG_MGR} installed" + echo_if_fail pipx install virtualenv || return 1 + echo_if_fail pipx ensurepath || return 1 + echo_pass "pipx is installed" + # shellcheck disable=SC1091 + test -f "${HOME}/.cargo/env" && source "${HOME}/.cargo/env" + + if ! command -v cargo >/dev/null; then + echo_warn "missing cargo" + + if ! command -v rustup >/dev/null; then + echo_warn "missing rustup" + echo_warn "installing rustup" + curl https://sh.rustup.rs -sSf | sh -s -- -y + # shellcheck disable=SC2016 + grep -q 'source "${HOME}/.cargo/env"' "${HOME}/.bashrc" || \ + echo 'source "${HOME}/.cargo/env"' >> "${HOME}/.bashrc" + # shellcheck disable=SC1091 + source "${HOME}/.bashrc" + fi + fi + + echo_if_fail rustup default stable + echo_if_fail rustup update stable + echo_pass "rustup is installed" + echo_if_fail cargo install cargo-c || return 1 + echo_pass "cargo-c is installed" + echo_pass "all required packages installed" + return 0 +} + +FB_FUNC_NAMES+=('install_deps') +FB_FUNC_DESCS['install_deps']='install required dependencies' +install_deps() { + unset SUDO + test "$(id -u)" -eq 0 || SUDO=sudo + determine_pkg_mgr || return 1 + check_for_req_pkgs || return 1 +} \ No newline at end of file