diff --git a/docker/archlinux.dockerfile b/docker/archlinux.dockerfile index d06c2ea5..6d23160c 100644 --- a/docker/archlinux.dockerfile +++ b/docker/archlinux.dockerfile @@ -17,26 +17,11 @@ pacman -Syu --disable-download-timeout --noconfirm pacman -Scc --noconfirm _DEPS -FROM sunshine-base AS sunshine-build - -ARG BRANCH -ARG BUILD_VERSION -ARG COMMIT -ARG CLONE_URL -# note: BUILD_VERSION may be blank - -ENV BRANCH=${BRANCH} -ENV BUILD_VERSION=${BUILD_VERSION} -ENV COMMIT=${COMMIT} -ENV CLONE_URL=${CLONE_URL} - -# PKGBUILD options -ENV _use_cuda=true -ENV _run_unit_tests=true -ENV _support_headless_testing=true +FROM sunshine-base AS sunshine-deps SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# Install dependencies first - this layer will be cached RUN <<_SETUP #!/bin/bash set -e @@ -60,6 +45,26 @@ pacman -Syu --disable-download-timeout --needed --noconfirm \ pacman -Scc --noconfirm _SETUP +FROM sunshine-deps AS sunshine-build + +ARG BRANCH +ARG BUILD_VERSION +ARG COMMIT +ARG CLONE_URL +# note: BUILD_VERSION may be blank + +ENV BRANCH=${BRANCH} +ENV BUILD_VERSION=${BUILD_VERSION} +ENV COMMIT=${COMMIT} +ENV CLONE_URL=${CLONE_URL} + +# PKGBUILD options +ENV _use_cuda=true +ENV _run_unit_tests=true +ENV _support_headless_testing=true + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + # Setup builder user USER builder diff --git a/docker/debian-trixie.dockerfile b/docker/debian-trixie.dockerfile index cec96910..78d8637e 100644 --- a/docker/debian-trixie.dockerfile +++ b/docker/debian-trixie.dockerfile @@ -9,7 +9,29 @@ FROM ${BASE}:${TAG} AS sunshine-base ENV DEBIAN_FRONTEND=noninteractive -FROM sunshine-base AS sunshine-build +FROM sunshine-base AS sunshine-deps + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Copy only the build script and necessary files first for better layer caching +WORKDIR /build/sunshine/ +COPY --link scripts/linux_build.sh ./scripts/linux_build.sh +COPY --link packaging/linux/patches/ ./packaging/linux/patches/ + +# Install dependencies first - this layer will be cached +RUN <<_DEPS +#!/bin/bash +set -e +chmod +x ./scripts/linux_build.sh +./scripts/linux_build.sh \ + --step=deps \ + --cuda-patches \ + --sudo-off +apt-get clean +rm -rf /var/lib/apt/lists/* +_DEPS + +FROM sunshine-deps AS sunshine-build ARG BRANCH ARG BUILD_VERSION @@ -20,25 +42,31 @@ ENV BRANCH=${BRANCH} ENV BUILD_VERSION=${BUILD_VERSION} ENV COMMIT=${COMMIT} -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# copy repository -WORKDIR /build/sunshine/ +# Now copy the full repository COPY --link .. . -# cmake and cpack +# Configure, validate, build and package RUN <<_BUILD #!/bin/bash set -e -chmod +x ./scripts/linux_build.sh ./scripts/linux_build.sh \ - --cuda-patches \ + --step=cmake \ --publisher-name='LizardByte' \ --publisher-website='https://app.lizardbyte.dev' \ --publisher-issue-url='https://app.lizardbyte.dev/support' \ --sudo-off -apt-get clean -rm -rf /var/lib/apt/lists/* + +./scripts/linux_build.sh \ + --step=validation \ + --sudo-off + +./scripts/linux_build.sh \ + --step=build \ + --sudo-off + +./scripts/linux_build.sh \ + --step=package \ + --sudo-off _BUILD # run tests diff --git a/docker/ubuntu-22.04.dockerfile b/docker/ubuntu-22.04.dockerfile index d8a8df42..1693367c 100644 --- a/docker/ubuntu-22.04.dockerfile +++ b/docker/ubuntu-22.04.dockerfile @@ -9,7 +9,28 @@ FROM ${BASE}:${TAG} AS sunshine-base ENV DEBIAN_FRONTEND=noninteractive -FROM sunshine-base AS sunshine-build +FROM sunshine-base AS sunshine-deps + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Copy only the build script first for better layer caching +WORKDIR /build/sunshine/ +COPY --link scripts/linux_build.sh ./scripts/linux_build.sh + +# Install dependencies first - this layer will be cached +RUN <<_DEPS +#!/bin/bash +set -e +chmod +x ./scripts/linux_build.sh +./scripts/linux_build.sh \ + --step=deps \ + --ubuntu-test-repo \ + --sudo-off +apt-get clean +rm -rf /var/lib/apt/lists/* +_DEPS + +FROM sunshine-deps AS sunshine-build ARG BRANCH ARG BUILD_VERSION @@ -20,25 +41,31 @@ ENV BRANCH=${BRANCH} ENV BUILD_VERSION=${BUILD_VERSION} ENV COMMIT=${COMMIT} -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# copy repository -WORKDIR /build/sunshine/ +# Now copy the full repository COPY --link .. . -# cmake and cpack +# Configure, validate, build and package RUN <<_BUILD #!/bin/bash set -e -chmod +x ./scripts/linux_build.sh ./scripts/linux_build.sh \ + --step=cmake \ --publisher-name='LizardByte' \ --publisher-website='https://app.lizardbyte.dev' \ --publisher-issue-url='https://app.lizardbyte.dev/support' \ - --sudo-off \ - --ubuntu-test-repo -apt-get clean -rm -rf /var/lib/apt/lists/* + --sudo-off + +./scripts/linux_build.sh \ + --step=validation \ + --sudo-off + +./scripts/linux_build.sh \ + --step=build \ + --sudo-off + +./scripts/linux_build.sh \ + --step=package \ + --sudo-off _BUILD # run tests diff --git a/docker/ubuntu-24.04.dockerfile b/docker/ubuntu-24.04.dockerfile index e11f18b2..6c0f82fc 100644 --- a/docker/ubuntu-24.04.dockerfile +++ b/docker/ubuntu-24.04.dockerfile @@ -9,7 +9,27 @@ FROM ${BASE}:${TAG} AS sunshine-base ENV DEBIAN_FRONTEND=noninteractive -FROM sunshine-base AS sunshine-build +FROM sunshine-base AS sunshine-deps + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Copy only the build script first for better layer caching +WORKDIR /build/sunshine/ +COPY --link scripts/linux_build.sh ./scripts/linux_build.sh + +# Install dependencies first - this layer will be cached +RUN <<_DEPS +#!/bin/bash +set -e +chmod +x ./scripts/linux_build.sh +./scripts/linux_build.sh \ + --step=deps \ + --sudo-off +apt-get clean +rm -rf /var/lib/apt/lists/* +_DEPS + +FROM sunshine-deps AS sunshine-build ARG BRANCH ARG BUILD_VERSION @@ -20,24 +40,31 @@ ENV BRANCH=${BRANCH} ENV BUILD_VERSION=${BUILD_VERSION} ENV COMMIT=${COMMIT} -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# copy repository -WORKDIR /build/sunshine/ +# Now copy the full repository COPY --link .. . -# cmake and cpack +# Configure, validate, build and package RUN <<_BUILD #!/bin/bash set -e -chmod +x ./scripts/linux_build.sh ./scripts/linux_build.sh \ + --step=cmake \ --publisher-name='LizardByte' \ --publisher-website='https://app.lizardbyte.dev' \ --publisher-issue-url='https://app.lizardbyte.dev/support' \ --sudo-off -apt-get clean -rm -rf /var/lib/apt/lists/* + +./scripts/linux_build.sh \ + --step=validation \ + --sudo-off + +./scripts/linux_build.sh \ + --step=build \ + --sudo-off + +./scripts/linux_build.sh \ + --step=package \ + --sudo-off _BUILD # run tests diff --git a/scripts/linux_build.sh b/scripts/linux_build.sh index c45cbb2e..cc1be275 100755 --- a/scripts/linux_build.sh +++ b/scripts/linux_build.sh @@ -1,6 +1,13 @@ #!/bin/bash set -e +# Version requirements - centralized for easy maintenance +cmake_min="3.25.0" +target_cmake_version="3.30.1" +doxygen_min="1.10.0" +_doxygen_min="${doxygen_min//\./_}" # Convert dots to underscores for URL +doxygen_max="1.12.0" + # Default value for arguments appimage_build=0 cuda_patches=0 @@ -14,6 +21,55 @@ skip_libva=0 skip_package=0 sudo_cmd="sudo" ubuntu_test_repo=0 +step="all" + +# common variables +gcc_alternative_files=( + "gcc" + "g++" + "gcov" + "gcc-ar" + "gcc-ranlib" +) + +# Reusable function to detect nvcc path +function detect_nvcc_path() { + local nvcc_path="" + + # First check for system-installed CUDA + nvcc_path=$(command -v nvcc 2>/dev/null) || true + if [ -n "$nvcc_path" ]; then + echo "$nvcc_path" + return 0 + fi + + # Then check for locally installed CUDA in build directory + if [ -f "${build_dir}/cuda/bin/nvcc" ]; then + echo "${build_dir}/cuda/bin/nvcc" + return 0 + fi + + # No CUDA found + return 1 +} + +# Reusable function to setup NVM environment +function setup_nvm_environment() { + # Only setup NVM if it should be used for this distro + if [ "$nvm_node" == 1 ]; then + # Check if NVM is installed and source it + if [ -f "$HOME/.nvm/nvm.sh" ]; then + # shellcheck source=/dev/null + source "$HOME/.nvm/nvm.sh" + # Use the default node version installed by NVM + nvm use default 2>/dev/null || nvm use node 2>/dev/null || true + echo "Using NVM Node.js version: $(node --version 2>/dev/null || echo 'not available')" + echo "Using NVM npm version: $(npm --version 2>/dev/null || echo 'not available')" + else + echo "NVM not found, using system Node.js if available" + fi + fi +} function _usage() { local exit_code=$1 @@ -40,6 +96,16 @@ Options: --skip-libva Skip libva installation. This will automatically be enabled if passing --appimage-build. --skip-package Skip creating DEB, or RPM package. --ubuntu-test-repo Install ppa:ubuntu-toolchain-r/test repo on Ubuntu. + --step Which step(s) to run: deps, cmake, validation, build, package, cleanup, or all (default: all) + +Steps: + deps Install dependencies only + cmake Run cmake configure only + validation Run validation commands only + build Build the project only + package Create packages only + cleanup Cleanup alternatives and backups only + all Run all steps (default) EOF exit "$exit_code" @@ -78,6 +144,9 @@ while getopts ":hs-:" opt; do skip-package) skip_package=1 ;; sudo-off) sudo_cmd="" ;; ubuntu-test-repo) ubuntu_test_repo=1 ;; + step=*) + step="${OPTARG#*=}" + ;; *) echo "Invalid option: --${OPTARG}" 1>&2 _usage 1 @@ -261,15 +330,8 @@ function add_fedora_deps() { } function install_cuda() { - nvcc_path=$(command -v nvcc 2>/dev/null) || true - if [ -n "$nvcc_path" ]; then - echo "found system cuda" - return - fi - # check if we need to install cuda - if [ -f "${build_dir}/cuda/bin/nvcc" ]; then - nvcc_path="${build_dir}/cuda/bin/nvcc" - echo "found local cuda" + # Check if CUDA is already available + if detect_nvcc_path > /dev/null 2>&1; then return fi @@ -306,7 +368,6 @@ function install_cuda() { chmod a+x "${build_dir}/cuda.run" "${build_dir}/cuda.run" --silent --toolkit --toolkitpath="${build_dir}/cuda" --no-opengl-libs --no-man-page --no-drm rm "${build_dir}/cuda.run" - nvcc_path="${build_dir}/cuda/bin/nvcc" # run cuda patches if [ "$cuda_patches" == 1 ]; then @@ -361,7 +422,116 @@ if [[ "$(printf '%s\n' "$installed_version" "$min_version" | sort -V | head -n1) fi } -function run_install() { +function run_step_deps() { + echo "Running step: Install dependencies" + + # Update the package list + $package_update_command + + if [ "$distro" == "arch" ]; then + add_arch_deps + elif [ "$distro" == "debian" ]; then + add_debian_deps + elif [ "$distro" == "ubuntu" ]; then + add_ubuntu_deps + elif [ "$distro" == "fedora" ]; then + add_fedora_deps + ${sudo_cmd} dnf group install "$dev_tools_group" -y + fi + + # Install the dependencies + $package_install_command "${dependencies[@]}" + + # reload the environment + # shellcheck source=/dev/null + source ~/.bashrc + + #set gcc version based on distros + if [ "$distro" == "arch" ]; then + export CC=gcc-14 + export CXX=g++-14 + elif [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then + for file in "${gcc_alternative_files[@]}"; do + file_path="/etc/alternatives/$file" + if [ -e "$file_path" ]; then + ${sudo_cmd} mv "$file_path" "$file_path.bak" + fi + done + + ${sudo_cmd} update-alternatives --install \ + /usr/bin/gcc gcc /usr/bin/gcc-${gcc_version} 100 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-${gcc_version} \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-${gcc_version} \ + --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-${gcc_version} \ + --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-${gcc_version} + fi + + # compile cmake if the version is too low + if ! check_version "cmake" "$cmake_min" "inf"; then + cmake_prefix="https://github.com/Kitware/CMake/releases/download/v" + if [ "$architecture" == "x86_64" ]; then + cmake_arch="x86_64" + elif [ "$architecture" == "aarch64" ]; then + cmake_arch="aarch64" + fi + url="${cmake_prefix}${target_cmake_version}/cmake-${target_cmake_version}-linux-${cmake_arch}.sh" + echo "cmake url: ${url}" + wget "$url" --progress=bar:force:noscroll -q --show-progress -O "${build_dir}/cmake.sh" + ${sudo_cmd} sh "${build_dir}/cmake.sh" --skip-license --prefix=/usr/local + echo "cmake installed, version:" + cmake --version + fi + + # compile doxygen if version is too low + if ! check_version "doxygen" "$doxygen_min" "$doxygen_max"; then + if [ "${SUNSHINE_COMPILE_DOXYGEN}" == "true" ]; then + echo "Compiling doxygen" + doxygen_url="https://github.com/doxygen/doxygen/releases/download/Release_${_doxygen_min}/doxygen-${doxygen_min}.src.tar.gz" + echo "doxygen url: ${doxygen_url}" + pushd "${build_dir}" + wget "$doxygen_url" --progress=bar:force:noscroll -q --show-progress -O "doxygen.tar.gz" + tar -xzf "doxygen.tar.gz" + cd "doxygen-${doxygen_min}" + cmake -DCMAKE_BUILD_TYPE=Release -G="Ninja" -B="build" -S="." + ninja -C "build" -j"${num_processors}" + ${sudo_cmd} ninja -C "build" install + popd + else + echo "Doxygen version not in range, skipping docs" + # Note: cmake_args will be set in cmake step + fi + fi + + # install node from nvm + if [ "$nvm_node" == 1 ]; then + nvm_url="https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh" + echo "nvm url: ${nvm_url}" + wget -qO- ${nvm_url} | bash + + # shellcheck source=/dev/null # we don't care that shellcheck cannot find nvm.sh + source "$HOME/.nvm/nvm.sh" + nvm install node + nvm use node + fi + + # run the cuda install + if [ "$skip_cuda" == 0 ]; then + install_cuda + fi +} + +function run_step_cmake() { + echo "Running step: CMake configure" + + # Setup NVM environment if needed (for web UI builds) + setup_nvm_environment + + # Detect CUDA path using the reusable function + nvcc_path="" + if [ "$skip_cuda" == 0 ]; then + nvcc_path=$(detect_nvcc_path) + fi + # prepare CMAKE args cmake_args=( "-B=build" @@ -392,113 +562,19 @@ function run_install() { cmake_args+=("-DSUNSHINE_PUBLISHER_ISSUE_URL='${publisher_issue_url}'") fi - # Update the package list - $package_update_command - - if [ "$distro" == "arch" ]; then - add_arch_deps - elif [ "$distro" == "debian" ]; then - add_debian_deps - elif [ "$distro" == "ubuntu" ]; then - add_ubuntu_deps - elif [ "$distro" == "fedora" ]; then - add_fedora_deps - ${sudo_cmd} dnf group install "$dev_tools_group" -y - fi - - # Install the dependencies - $package_install_command "${dependencies[@]}" - - # reload the environment - # shellcheck source=/dev/null - source ~/.bashrc - - gcc_alternative_files=( - "gcc" - "g++" - "gcov" - "gcc-ar" - "gcc-ranlib" - ) - - #set gcc version based on distros - if [ "$distro" == "arch" ]; then - export CC=gcc-14 - export CXX=g++-14 - elif [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then - for file in "${gcc_alternative_files[@]}"; do - file_path="/etc/alternatives/$file" - if [ -e "$file_path" ]; then - ${sudo_cmd} mv "$file_path" "$file_path.bak" - fi - done - - ${sudo_cmd} update-alternatives --install \ - /usr/bin/gcc gcc /usr/bin/gcc-${gcc_version} 100 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-${gcc_version} \ - --slave /usr/bin/gcov gcov /usr/bin/gcov-${gcc_version} \ - --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-${gcc_version} \ - --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-${gcc_version} - fi - - # compile cmake if the version is too low - cmake_min="3.25.0" - target_cmake_version="3.30.1" - if ! check_version "cmake" "$cmake_min" "inf"; then - cmake_prefix="https://github.com/Kitware/CMake/releases/download/v" - if [ "$architecture" == "x86_64" ]; then - cmake_arch="x86_64" - elif [ "$architecture" == "aarch64" ]; then - cmake_arch="aarch64" - fi - url="${cmake_prefix}${target_cmake_version}/cmake-${target_cmake_version}-linux-${cmake_arch}.sh" - echo "cmake url: ${url}" - wget "$url" --progress=bar:force:noscroll -q --show-progress -O "${build_dir}/cmake.sh" - ${sudo_cmd} sh "${build_dir}/cmake.sh" --skip-license --prefix=/usr/local - echo "cmake installed, version:" - cmake --version - fi - - # compile doxygen if version is too low - doxygen_min="1.10.0" - _doxygen_min="1_10_0" - doxygen_max="1.12.0" + # Handle doxygen docs flag if ! check_version "doxygen" "$doxygen_min" "$doxygen_max"; then - if [ "${SUNSHINE_COMPILE_DOXYGEN}" == "true" ]; then - echo "Compiling doxygen" - doxygen_url="https://github.com/doxygen/doxygen/releases/download/Release_${_doxygen_min}/doxygen-${doxygen_min}.src.tar.gz" - echo "doxygen url: ${doxygen_url}" - pushd "${build_dir}" - wget "$doxygen_url" --progress=bar:force:noscroll -q --show-progress -O "doxygen.tar.gz" - tar -xzf "doxygen.tar.gz" - cd "doxygen-${doxygen_min}" - cmake -DCMAKE_BUILD_TYPE=Release -G="Ninja" -B="build" -S="." - ninja -C "build" -j"${num_processors}" - ${sudo_cmd} ninja -C "build" install - popd - else - echo "Doxygen version not in range, skipping docs" + if [ "${SUNSHINE_COMPILE_DOXYGEN}" != "true" ]; then cmake_args+=("-DBUILD_DOCS=OFF") fi fi - # install node from nvm - if [ "$nvm_node" == 1 ]; then - nvm_url="https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh" - echo "nvm url: ${nvm_url}" - wget -qO- ${nvm_url} | bash - - # shellcheck source=/dev/null # we don't care that shellcheck cannot find nvm.sh - source "$HOME/.nvm/nvm.sh" - nvm install node - nvm use node - fi - - # run the cuda install + # Handle CUDA if [ "$skip_cuda" == 0 ]; then - install_cuda cmake_args+=("-DSUNSHINE_ENABLE_CUDA=ON") - cmake_args+=("-DCMAKE_CUDA_COMPILER:PATH=$nvcc_path") + if [ -n "$nvcc_path" ]; then + cmake_args+=("-DCMAKE_CUDA_COMPILER:PATH=$nvcc_path") + fi else cmake_args+=("-DSUNSHINE_ENABLE_CUDA=OFF") fi @@ -508,6 +584,10 @@ function run_install() { echo "cmake args:" echo "${cmake_args[@]}" cmake "${cmake_args[@]}" +} + +function run_step_validation() { + echo "Running step: Validation" # Run appstream validation, etc. appstreamcli validate "build/dev.lizardbyte.app.Sunshine.metainfo.xml" @@ -516,9 +596,20 @@ function run_install() { if [ "$appimage_build" == 0 ]; then desktop-file-validate "build/dev.lizardbyte.app.Sunshine.terminal.desktop" fi +} + +function run_step_build() { + echo "Running step: Build" + + # Setup NVM environment if needed (for web UI builds) + setup_nvm_environment # Build the project ninja -C "build" +} + +function run_step_package() { + echo "Running step: Package" # Create the package if [ "$skip_package" == 0 ]; then @@ -528,6 +619,10 @@ function run_install() { cpack -G RPM --config ./build/CPackConfig.cmake fi fi +} + +function run_step_cleanup() { + echo "Running step: Cleanup" if [ "$skip_cleanup" == 0 ]; then # Restore the original gcc alternatives @@ -548,6 +643,42 @@ function run_install() { fi } +function run_install() { + case "$step" in + deps) + run_step_deps + ;; + cmake) + run_step_cmake + ;; + validation) + run_step_validation + ;; + build) + run_step_build + ;; + package) + run_step_package + ;; + cleanup) + run_step_cleanup + ;; + all) + run_step_deps + run_step_cmake + run_step_validation + run_step_build + run_step_package + run_step_cleanup + ;; + *) + echo "Invalid step: $step" + echo "Valid steps are: deps, cmake, validation, build, package, cleanup, all" + exit 1 + ;; + esac +} + # Determine the OS and call the appropriate function cat /etc/os-release