#!/usr/bin/env bash # # tools/bootstrap.sh — install every build/test dependency for Velox Download Manager. # # Target: a clean Ubuntu 26.04 LTS machine. Safe to re-run (apt is idempotent). # Owned by lane PKG/QA. Keep this in sync with the table in README.md "Toolchain bootstrap". # # ./tools/bootstrap.sh # everything needed to build + test # ./tools/bootstrap.sh --with-clang # also install clang/LLVM for the libFuzzer targets # ./tools/bootstrap.sh --packaging # also install .deb tooling (lane PKG, M6) # ./tools/bootstrap.sh --check # verify versions only, install nothing set -euo pipefail WITH_CLANG=0 WITH_PACKAGING=0 CHECK_ONLY=0 for arg in "$@"; do case "$arg" in --with-clang) WITH_CLANG=1 ;; --packaging) WITH_PACKAGING=1 ;; --check) CHECK_ONLY=1 ;; -h|--help) sed -n '3,12p' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; *) echo "bootstrap: unknown option '$arg'" >&2; exit 2 ;; esac done log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } # --- sanity -------------------------------------------------------------------- [[ "$(uname -s)" == "Linux" ]] || die "this project targets Linux (Ubuntu 26.04)." if [[ -r /etc/os-release ]]; then . /etc/os-release if [[ "${ID:-}" != "ubuntu" ]]; then warn "distro is '${ID:-unknown}', not ubuntu — package names may differ." elif [[ "${VERSION_ID:-}" != "26.04" ]]; then warn "Ubuntu ${VERSION_ID:-?} detected; the project is developed against 26.04." fi fi command -v apt-get >/dev/null 2>&1 || die "apt-get not found; install deps manually (see README)." SUDO="" if [[ $EUID -ne 0 ]]; then command -v sudo >/dev/null 2>&1 || die "not root and no sudo; re-run as root." SUDO="sudo" fi # --- package sets ----------------------------------------------------------------- # Full list, so a clean machine gets everything even though the dev box already has most. APT_CORE=( build-essential # g++, make g++ # C++23 front end (need >= 13; 26.04 ships 15) gdb cmake # >= 3.28 (26.04 ships 4.2) ninja-build pkg-config git python3 # testserver, conformance helpers, codegen ca-certificates curl ) APT_LIBS=( libcurl4-openssl-dev # CORE: HTTP stack libsqlite3-dev # DAEMON: task store nlohmann-json3-dev # DAEMON/PROTO: JSON-RPC (never in core/) libssl-dev # CORE: SHA-256 / TLS bits libsecret-1-dev # DAEMON: Secret Service for credentials libavformat-dev # MEDIA (M4): HLS/DASH mux libavcodec-dev libavutil-dev ffmpeg # MEDIA (M4): runtime muxer ) APT_GUI=( qt6-base-dev qt6-tools-dev qt6-tools-dev-tools # lrelease/lupdate for i18n qt6-svg-dev # GUI: SVG icon rendering. Was libqt6svg6-dev, which has no # candidate on 26.04; qt6-svg-dev is the package that # ships the headers + Qt6SvgConfig.cmake find_package needs. ) APT_LINT=( clang-format clang-tidy ) APT_NODE=( nodejs # EXT + tools/mockd + conformance runner npm ) APT_PYTHON=( python3-jsonschema # tests/conformance: check_contract.py schema validation python3-referencing # tests/conformance: $ref registry for draft 2020-12 ) APT_CLANG=( clang # optional: libFuzzer targets (M7) llvm ) APT_PACKAGING=( debhelper dpkg-dev lintian devscripts fakeroot ) # --- install -------------------------------------------------------------------- PKGS=("${APT_CORE[@]}" "${APT_LIBS[@]}" "${APT_GUI[@]}" "${APT_LINT[@]}" "${APT_NODE[@]}" "${APT_PYTHON[@]}") [[ $WITH_CLANG -eq 1 ]] && PKGS+=("${APT_CLANG[@]}") [[ $WITH_PACKAGING -eq 1 ]] && PKGS+=("${APT_PACKAGING[@]}") if [[ $CHECK_ONLY -eq 0 ]]; then log "apt-get update" $SUDO apt-get update -qq log "installing ${#PKGS[@]} packages (idempotent; already-present ones are skipped)" DEBIAN_FRONTEND=noninteractive $SUDO apt-get install -y --no-install-recommends "${PKGS[@]}" else log "--check: skipping installation" fi # --- verify -------------------------------------------------------------------- log "verifying toolchain" fail=0 need_cmd() { if command -v "$1" >/dev/null 2>&1; then printf ' \033[32m✓\033[0m %-16s %s\n' "$1" "$("${@:2}" 2>&1 | head -1)" else printf ' \033[31m✗\033[0m %-16s MISSING\n' "$1" fail=1 fi } # Every apt name this script would install must resolve to an installable candidate on # THIS release. The checks below verify outcomes — binaries on $PATH, pkg-config modules — # on a box that already installed everything; none of them look at the APT_* names, which # is the one part of the script that breaks on a clean machine of the wrong release # (libqt6svg6-dev did exactly this on 26.04). See gui/docs/pkg-qa-requests-m1.md R2. # `apt-cache policy` needs no root and no network; it reads the lists apt already has. log "validating apt package names (${#PKGS[@]})" missing_pkgs=() for pkg in "${PKGS[@]}"; do # `apt-cache policy` exits 0 whether or not the name resolves; a name with no # installable version prints "Candidate: (none)". Grep the line, don't trust $?. cand="$(apt-cache policy "$pkg" 2>/dev/null | sed -n 's/^ Candidate: //p')" if [[ -z "$cand" || "$cand" == "(none)" ]]; then missing_pkgs+=("$pkg") fi done if (( ${#missing_pkgs[@]} == 0 )); then printf ' \033[32m✓\033[0m %-16s all %d resolve on %s\n' \ "apt names" "${#PKGS[@]}" "${VERSION_ID:-this release}" elif (( ${#missing_pkgs[@]} == ${#PKGS[@]} )); then warn "no apt candidate for ANY of the ${#PKGS[@]} packages — the apt lists are almost" warn "certainly stale, not the package set. Run 'apt-get update' and re-run --check." else for pkg in "${missing_pkgs[@]}"; do printf ' \033[31m✗\033[0m %-16s no installable candidate on %s\n' \ "$pkg" "${VERSION_ID:-this release}" done fail=1 fi need_cmd git git --version need_cmd cmake cmake --version need_cmd ninja ninja --version need_cmd g++ g++ --version need_cmd pkg-config pkg-config --version need_cmd python3 python3 --version need_cmd clang-format clang-format --version need_cmd clang-tidy clang-tidy --version need_cmd node node --version need_cmd npm npm --version [[ $WITH_CLANG -eq 1 ]] && need_cmd clang clang --version # Version floors that actually matter. if command -v cmake >/dev/null 2>&1; then cmake_ver="$(cmake --version | sed -n '1s/.* //p')" # `sort -V -C` exits 0 iff the input is already in ascending order, i.e. floor <= ver. if ! printf '3.28.0\n%s\n' "$cmake_ver" | sort -V -C; then warn "cmake $cmake_ver is below the 3.28 floor in CMakeLists.txt." fi # CMake >= 4 hard-errors on cmake_minimum_required() < 3.5 in any dependency. if printf '4.0.0\n%s\n' "$cmake_ver" | sort -V -C; then log "cmake $cmake_ver is 4.x — dependencies with a pre-3.5 CMake floor will fail to configure." fi fi if command -v g++ >/dev/null 2>&1; then gxx_major="$(g++ -dumpversion | cut -d. -f1)" if (( gxx_major < 13 )); then warn "g++ $gxx_major lacks the C++23 support this project needs (>= 13)." fi echo '#include #include #if __cpp_lib_expected < 202202L #error no std::expected #endif int main(){}' > /tmp/vdm_bootstrap_cxx23.$$.cpp if g++ -std=c++23 -fsyntax-only /tmp/vdm_bootstrap_cxx23.$$.cpp 2>/dev/null; then printf ' \033[32m✓\033[0m %-16s std::expected / -std=c++23 OK\n' "c++23" else printf ' \033[31m✗\033[0m %-16s -std=c++23 with failed\n' "c++23" fail=1 fi rm -f /tmp/vdm_bootstrap_cxx23.$$.cpp fi if command -v node >/dev/null 2>&1; then node_major="$(node --version | sed 's/^v//; s/\..*//')" if (( node_major < 20 )); then warn "node $node_major may be too old for the extension toolchain; consider nvm (>= 20)." fi fi # Python modules the conformance suite's static runner imports directly. if command -v python3 >/dev/null 2>&1; then for mod in jsonschema referencing; do if python3 -c "import $mod" 2>/dev/null; then printf ' \033[32m✓\033[0m %-16s %s\n' "py:$mod" \ "$(python3 -c "import importlib.metadata as m; print(m.version('$mod'))" 2>/dev/null)" else printf ' \033[31m✗\033[0m %-16s python3 can'\''t import it\n' "py:$mod" fail=1 fi done fi # Qt / library dev packages: check via pkg-config where possible. for mod in Qt6Core Qt6Widgets Qt6Svg libcurl sqlite3 libssl libsecret-1 libavformat; do if pkg-config --exists "$mod" 2>/dev/null; then printf ' \033[32m✓\033[0m %-16s %s\n' "$mod" "$(pkg-config --modversion "$mod")" else printf ' \033[31m✗\033[0m %-16s pkg-config can'\''t find it\n' "$mod" fail=1 fi done echo if [[ $fail -ne 0 ]]; then die "toolchain incomplete — see the ✗ lines above." fi log "toolchain OK. Next: cmake --preset dev && cmake --build --preset dev"