diff --git a/.github/workflows/ci-alpine.yml b/.github/workflows/ci-alpine.yml new file mode 100644 index 00000000000..287beab43bd --- /dev/null +++ b/.github/workflows/ci-alpine.yml @@ -0,0 +1,218 @@ +--- +name: CI-Alpine +permissions: {} + +on: + workflow_call: + inputs: + release_commit: + required: true + type: string + release_version: + required: true + type: string + +jobs: + build_alpine: + name: ${{ matrix.name }} + env: + BUILD_DEPS_DIR: cmake-build-alpine-build-deps + BUILD_DIR: cmake-build-alpine + permissions: + contents: read + runs-on: ${{ matrix.runner }} + container: + image: alpine:3.22 + strategy: + fail-fast: false + matrix: + include: + - name: Alpine-x86_64 + asset_name: Alpine-x86_64-ffmpeg.tar.gz + runner: ubuntu-latest + - name: Alpine-aarch64 + asset_name: Alpine-aarch64-ffmpeg.tar.gz + runner: ubuntu-24.04-arm + steps: + - name: Fix arm64 Alpine container + if: runner.arch == 'ARM64' + uses: laverdet/alpine-arm64@7f0f72ee2f71eb2324e5888e8b6e42b1b53e6160 # v1.0.0 + + - name: Install dependencies + run: | + set -eux + apk add --no-cache \ + appstream \ + appstream-glib \ + autoconf \ + automake \ + bash \ + build-base \ + cmake \ + curl \ + curl-dev \ + curl-static \ + desktop-file-utils \ + dpkg \ + file \ + freetype-dev \ + gcovr \ + git \ + glib-dev \ + glslang \ + gnutls-dev \ + lame-dev \ + libass-dev \ + libcap-dev \ + libcap-static \ + libdrm-dev \ + libevdev-dev \ + libtool \ + libva-dev \ + libvorbis-dev \ + libx11-dev \ + libxcb-dev \ + libxfixes-dev \ + libxrandr-dev \ + libxtst-dev \ + linux-headers \ + meson \ + miniupnpc-dev \ + nasm \ + ninja-build \ + nodejs \ + npm \ + numactl-dev \ + openssl-dev \ + openssl-libs-static \ + opus-dev \ + pkgconf \ + pulseaudio-dev \ + py3-jinja2 \ + py3-pip \ + py3-setuptools \ + python3 \ + rpm \ + samurai \ + sdl2-dev \ + shaderc-dev \ + tar \ + texinfo \ + vulkan-loader-dev \ + wayland-dev \ + wayland-static \ + wget \ + xrandr \ + xvfb \ + xvfb-run \ + zlib-dev + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + submodules: recursive + + - name: Configure Alpine build + run: | + set -eux + cmake -B "${BUILD_DIR}" -S . -G Ninja \ + -DBUILD_DOCS=OFF \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DFFMPEG_RELEASE_ASSET_NAME="${{ matrix.asset_name }}" \ + -DSUNSHINE_ASSETS_DIR=share/sunshine \ + -DSUNSHINE_ENABLE_CUDA=OFF \ + -DSUNSHINE_ENABLE_STATIC_LINK=ON \ + -DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \ + -DSUNSHINE_LINUX_PACKAGE_RUNTIME_DEPS=OFF \ + -DSUNSHINE_PREFER_STATIC_LIBS=ON + + - name: Validate desktop metadata + working-directory: ${{ env.BUILD_DIR }} + run: | + set -eux + appstreamcli validate dev.lizardbyte.app.Sunshine.metainfo.xml + appstream-util validate dev.lizardbyte.app.Sunshine.metainfo.xml + desktop-file-validate dev.lizardbyte.app.Sunshine.desktop + desktop-file-validate dev.lizardbyte.app.Sunshine.terminal.desktop + + - name: Build Alpine + id: build + run: | + set -eux + echo "::add-matcher::.github/matchers/gcc.json" + cmake --build "${BUILD_DIR}" --parallel + echo "::remove-matcher owner=gcc::" + + - name: Verify static binaries + run: | + set -eux + file "${BUILD_DIR}/sunshine" + file "${BUILD_DIR}/tests/test_sunshine" + ldd "${BUILD_DIR}/sunshine" 2>&1 | tee "${BUILD_DIR}/sunshine-ldd.txt" + ldd "${BUILD_DIR}/tests/test_sunshine" 2>&1 | tee "${BUILD_DIR}/tests/test_sunshine-ldd.txt" + grep -Eq "not a dynamic executable|statically linked" "${BUILD_DIR}/sunshine-ldd.txt" + grep -Eq "not a dynamic executable|statically linked" "${BUILD_DIR}/tests/test_sunshine-ldd.txt" + + - name: Package Alpine + id: package + run: | + set -eux + cpack -G DEB --config "${BUILD_DIR}/CPackConfig.cmake" + cpack -G RPM --config "${BUILD_DIR}/CPackConfig.cmake" + ls -la "${BUILD_DIR}/cpack_artifacts" + dpkg-deb --info "${BUILD_DIR}/cpack_artifacts/"*.deb + rpm -qip "${BUILD_DIR}/cpack_artifacts/"*.rpm + + - name: Copy build artifacts + if: steps.package.outcome == 'success' + run: | + set -eux + mkdir -p artifacts + cp "${BUILD_DIR}/sunshine" "artifacts/sunshine-${{ matrix.name }}" + cp "${BUILD_DIR}/cpack_artifacts/"*.deb artifacts/ + cp "${BUILD_DIR}/cpack_artifacts/"*.rpm artifacts/ + + - name: Run tests + id: test + working-directory: ${{ env.BUILD_DIR }}/tests + run: | + set -eux + xvfb-run -a ./test_sunshine --gtest_color=yes --gtest_output=xml:test_results.xml + + - name: Generate gcov report + id: test_report + if: >- + always() && + (steps.test.outcome == 'success' || steps.test.outcome == 'failure') + working-directory: ${{ env.BUILD_DIR }} + run: | + set -eux + gcovr --gcov-executable gcov . -r ../src \ + --exclude-noncode-lines \ + --exclude-throw-branches \ + --exclude-unreachable-branches \ + --verbose \ + --xml-pretty \ + -o coverage.xml + + - name: Upload coverage artifact + if: >- + always() && + (steps.test_report.outcome == 'success') + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: coverage-${{ matrix.name }} + path: | + ${{ env.BUILD_DIR }}/coverage.xml + ${{ env.BUILD_DIR }}/tests/test_results.xml + if-no-files-found: error + + - name: Upload Artifacts + if: >- + always() && + (steps.package.outcome == 'success') + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: build-${{ matrix.name }} + path: artifacts/ + if-no-files-found: error diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 667f4f262e7..284d8447aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,6 +129,16 @@ jobs: release_commit: ${{ needs.release-setup.outputs.release_commit }} release_version: ${{ needs.release-setup.outputs.release_version }} + build-alpine: + name: Alpine + needs: release-setup + permissions: + contents: read + uses: ./.github/workflows/ci-alpine.yml + with: + release_commit: ${{ needs.release-setup.outputs.release_commit }} + release_version: ${{ needs.release-setup.outputs.release_version }} + build-linux-copr: name: Linux Copr if: github.event_name != 'push' # releases are handled directly in ci-copr.yml @@ -188,6 +198,7 @@ jobs: - build-freebsd - build-linux - build-archlinux + - build-alpine - build-linux-flatpak - build-macos - build-homebrew @@ -211,6 +222,12 @@ jobs: - name: Archlinux coverage: true pr: true + - name: Alpine-x86_64 + coverage: true + pr: true + - name: Alpine-aarch64 + coverage: true + pr: true - name: macOS-arm64 coverage: true pr: true @@ -290,6 +307,7 @@ jobs: needs: - release-setup - build-archlinux + - build-alpine - build-docker - build-freebsd - build-homebrew diff --git a/cmake/FindLIBCAP.cmake b/cmake/FindLIBCAP.cmake index 80ef7409841..f96ebc7ee56 100644 --- a/cmake/FindLIBCAP.cmake +++ b/cmake/FindLIBCAP.cmake @@ -14,7 +14,8 @@ pkg_check_modules(PC_LIBCAP libcap) set(LIBCAP_DEFINITIONS ${PC_LIBCAP_CFLAGS}) find_path(LIBCAP_INCLUDE_DIRS sys/capability.h PATHS ${PC_LIBCAP_INCLUDEDIR} ${PC_LIBCAP_INCLUDE_DIRS}) -find_library(LIBCAP_LIBRARIES NAMES libcap.so PATHS ${PC_LIBCAP_LIBDIR} ${PC_LIBCAP_LIBRARY_DIRS}) +find_library(LIBCAP_LIBRARIES NAMES ${PC_LIBCAP_LIBRARIES} cap libcap + PATHS ${PC_LIBCAP_LIBDIR} ${PC_LIBCAP_LIBRARY_DIRS}) mark_as_advanced(LIBCAP_INCLUDE_DIRS LIBCAP_LIBRARIES) include(FindPackageHandleStandardArgs) diff --git a/cmake/FindLIBDRM.cmake b/cmake/FindLIBDRM.cmake index 6e050018460..ee92c286ad2 100644 --- a/cmake/FindLIBDRM.cmake +++ b/cmake/FindLIBDRM.cmake @@ -14,7 +14,8 @@ pkg_check_modules(PC_LIBDRM libdrm) set(LIBDRM_DEFINITIONS ${PC_LIBDRM_CFLAGS}) find_path(LIBDRM_INCLUDE_DIRS drm.h PATHS ${PC_LIBDRM_INCLUDEDIR} ${PC_LIBDRM_INCLUDE_DIRS} PATH_SUFFIXES libdrm) -find_library(LIBDRM_LIBRARIES NAMES libdrm.so PATHS ${PC_LIBDRM_LIBDIR} ${PC_LIBDRM_LIBRARY_DIRS}) +find_library(LIBDRM_LIBRARIES NAMES ${PC_LIBDRM_LIBRARIES} drm libdrm + PATHS ${PC_LIBDRM_LIBDIR} ${PC_LIBDRM_LIBRARY_DIRS}) mark_as_advanced(LIBDRM_INCLUDE_DIRS LIBDRM_LIBRARIES) include(FindPackageHandleStandardArgs) diff --git a/cmake/dependencies/ffmpeg.cmake b/cmake/dependencies/ffmpeg.cmake index 291a6e5e84a..fe777fc3b0f 100644 --- a/cmake/dependencies/ffmpeg.cmake +++ b/cmake/dependencies/ffmpeg.cmake @@ -68,7 +68,11 @@ if(NOT DEFINED FFMPEG_PREPARED_BINARIES) set(FFMPEG_PREPARED_BINARIES "${FFMPEG_EXTRACT_DIR}/ffmpeg") # Set the archive filename based on architecture - set(FFMPEG_ARCHIVE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}-ffmpeg.tar.gz") + if(NOT DEFINED FFMPEG_RELEASE_ASSET_NAME) + set(FFMPEG_ARCHIVE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}-ffmpeg.tar.gz") + else() + set(FFMPEG_ARCHIVE_NAME "${FFMPEG_RELEASE_ASSET_NAME}") + endif() set(FFMPEG_ARCHIVE_PATH "${FFMPEG_VERSION_DIR}/${FFMPEG_ARCHIVE_NAME}") set(FFMPEG_DOWNLOAD_URL "${FFMPEG_RELEASE_URL}/${FFMPEG_ARCHIVE_NAME}") diff --git a/cmake/dependencies/libevdev_Sunshine.cmake b/cmake/dependencies/libevdev_Sunshine.cmake index 1358c339e5e..a284bda1d33 100644 --- a/cmake/dependencies/libevdev_Sunshine.cmake +++ b/cmake/dependencies/libevdev_Sunshine.cmake @@ -10,7 +10,8 @@ if(PC_EVDEV_FOUND) find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h HINTS ${PC_EVDEV_INCLUDE_DIRS} ${PC_EVDEV_INCLUDEDIR}) find_library(EVDEV_LIBRARY - NAMES evdev libevdev) + NAMES ${PC_EVDEV_LIBRARIES} evdev libevdev + HINTS ${PC_EVDEV_LIBRARY_DIRS} ${PC_EVDEV_LIBDIR}) else() include(ExternalProject) diff --git a/cmake/packaging/linux.cmake b/cmake/packaging/linux.cmake index 1d5e5141447..957f2b7d285 100644 --- a/cmake/packaging/linux.cmake +++ b/cmake/packaging/linux.cmake @@ -68,71 +68,78 @@ set(CPACK_RPM_USER_FILELIST "%caps(cap_sys_admin,cap_sys_nice+p) ${SUNSHINE_EXEC # Dependencies set(CPACK_DEB_COMPONENT_INSTALL ON) -set(CPACK_DEBIAN_PACKAGE_DEPENDS "\ - ${CPACK_DEB_PLATFORM_PACKAGE_DEPENDS} \ - debianutils, \ - libcap2, \ - libcurl4, \ - libdrm2, \ - libgbm1, \ - libevdev2, \ - libnuma1, \ - libopus0, \ - libpulse0, \ - libva2, \ - libva-drm2, \ - libwayland-client0, \ - libx11-6, \ - miniupnpc, \ - openssl | libssl3") -set(CPACK_RPM_PACKAGE_REQUIRES "\ - ${CPACK_RPM_PLATFORM_PACKAGE_REQUIRES} \ - libcap >= 2.22, \ - libcurl >= 7.0, \ - libdrm >= 2.4.97, \ - libevdev >= 1.5.6, \ - libopusenc >= 0.2.1, \ - libva >= 2.14.0, \ - libwayland-client >= 1.20.0, \ - libX11 >= 1.7.3.1, \ - mesa-libgbm >= 25.0.7, \ - miniupnpc >= 2.2.4, \ - numactl-libs >= 2.0.14, \ - openssl >= 3.0.2, \ - pulseaudio-libs >= 10.0, \ - which >= 2.21") -list(APPEND CPACK_FREEBSD_PACKAGE_DEPS - audio/opus - ftp/curl - devel/libevdev - multimedia/pipewire - net/avahi - net/miniupnpc - security/openssl - x11/libX11 -) - -if(NOT BOOST_USE_STATIC) +if(SUNSHINE_LINUX_PACKAGE_RUNTIME_DEPS) set(CPACK_DEBIAN_PACKAGE_DEPENDS "\ - ${CPACK_DEBIAN_PACKAGE_DEPENDS}, \ - libboost-filesystem${Boost_VERSION}, \ - libboost-locale${Boost_VERSION}, \ - libboost-log${Boost_VERSION}, \ - libboost-program-options${Boost_VERSION}") + ${CPACK_DEB_PLATFORM_PACKAGE_DEPENDS} \ + debianutils, \ + libcap2, \ + libcurl4, \ + libdrm2, \ + libgbm1, \ + libevdev2, \ + libnuma1, \ + libopus0, \ + libpulse0, \ + libva2, \ + libva-drm2, \ + libwayland-client0, \ + libx11-6, \ + miniupnpc, \ + openssl | libssl3") set(CPACK_RPM_PACKAGE_REQUIRES "\ - ${CPACK_RPM_PACKAGE_REQUIRES}, \ - boost-filesystem >= ${Boost_VERSION}, \ - boost-locale >= ${Boost_VERSION}, \ - boost-log >= ${Boost_VERSION}, \ - boost-program-options >= ${Boost_VERSION}") + ${CPACK_RPM_PLATFORM_PACKAGE_REQUIRES} \ + libcap >= 2.22, \ + libcurl >= 7.0, \ + libdrm >= 2.4.97, \ + libevdev >= 1.5.6, \ + libopusenc >= 0.2.1, \ + libva >= 2.14.0, \ + libwayland-client >= 1.20.0, \ + libX11 >= 1.7.3.1, \ + mesa-libgbm >= 25.0.7, \ + miniupnpc >= 2.2.4, \ + numactl-libs >= 2.0.14, \ + openssl >= 3.0.2, \ + pulseaudio-libs >= 10.0, \ + which >= 2.21") list(APPEND CPACK_FREEBSD_PACKAGE_DEPS - devel/boost-libs + audio/opus + ftp/curl + devel/libevdev + multimedia/pipewire + net/avahi + net/miniupnpc + security/openssl + x11/libX11 ) -endif() -# This should automatically figure out dependencies on packages -set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) -set(CPACK_RPM_PACKAGE_AUTOREQ ON) + if(NOT BOOST_USE_STATIC) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "\ + ${CPACK_DEBIAN_PACKAGE_DEPENDS}, \ + libboost-filesystem${Boost_VERSION}, \ + libboost-locale${Boost_VERSION}, \ + libboost-log${Boost_VERSION}, \ + libboost-program-options${Boost_VERSION}") + set(CPACK_RPM_PACKAGE_REQUIRES "\ + ${CPACK_RPM_PACKAGE_REQUIRES}, \ + boost-filesystem >= ${Boost_VERSION}, \ + boost-locale >= ${Boost_VERSION}, \ + boost-log >= ${Boost_VERSION}, \ + boost-program-options >= ${Boost_VERSION}") + list(APPEND CPACK_FREEBSD_PACKAGE_DEPS + devel/boost-libs + ) + endif() + + # This should automatically figure out dependencies on packages + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + set(CPACK_RPM_PACKAGE_AUTOREQ ON) +else() + set(CPACK_DEBIAN_PACKAGE_DEPENDS "") + set(CPACK_RPM_PACKAGE_REQUIRES "") + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF) + set(CPACK_RPM_PACKAGE_AUTOREQ OFF) +endif() # application icon install(FILES "${CMAKE_SOURCE_DIR}/sunshine.svg" diff --git a/cmake/prep/options.cmake b/cmake/prep/options.cmake index 6ce5b1ab0e0..4f02b8f45eb 100644 --- a/cmake/prep/options.cmake +++ b/cmake/prep/options.cmake @@ -10,6 +10,9 @@ set(SUNSHINE_PUBLISHER_ISSUE_URL "https://app.lizardbyte.dev/support" option(BUILD_DOCS "Build documentation" ON) option(BUILD_TESTS "Build tests" ON) option(NPM_OFFLINE "Use offline npm packages. You must ensure packages are in your npm cache." OFF) +option(SUNSHINE_PREFER_STATIC_LIBS "Prefer static libraries when they are available." OFF) +option(SUNSHINE_ENABLE_STATIC_LINK "Force static linking for Sunshine executables when supported." OFF) +option(SUNSHINE_LINUX_PACKAGE_RUNTIME_DEPS "Include Linux runtime dependency metadata in generated packages." ON) option(BUILD_WERROR "Enable -Werror flag." OFF) @@ -67,3 +70,12 @@ elseif(UNIX) # Linux option(SUNSHINE_ENABLE_PORTAL "Enable XDG portal grab if available" ON) endif() + +if(UNIX AND NOT APPLE AND SUNSHINE_PREFER_STATIC_LIBS) + set(PKG_CONFIG_USE_STATIC_LIBS ON) + + set(SUNSHINE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + list(REMOVE_ITEM SUNSHINE_FIND_LIBRARY_SUFFIXES ".a") + list(PREPEND SUNSHINE_FIND_LIBRARY_SUFFIXES ".a") + set(CMAKE_FIND_LIBRARY_SUFFIXES ${SUNSHINE_FIND_LIBRARY_SUFFIXES}) +endif() diff --git a/cmake/targets/common.cmake b/cmake/targets/common.cmake index 4e938f8baf0..dba3fc3ddbf 100644 --- a/cmake/targets/common.cmake +++ b/cmake/targets/common.cmake @@ -50,6 +50,10 @@ endif() target_compile_options(sunshine PRIVATE $<$:${SUNSHINE_COMPILE_OPTIONS}>;$<$:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301 +if(UNIX AND NOT APPLE AND SUNSHINE_ENABLE_STATIC_LINK) + target_link_options(sunshine PRIVATE -static -static-libgcc -static-libstdc++) +endif() + # Homebrew build fails the vite build if we set these environment variables if(${SUNSHINE_BUILD_HOMEBREW}) set(NPM_SOURCE_ASSETS_DIR "") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1d2ffad13d4..9fc21e9c6ca 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -170,6 +170,10 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC ${SUNSHINE_DEFINITIONS} ${TEST target_compile_options(${PROJECT_NAME} PRIVATE $<$:${SUNSHINE_COMPILE_OPTIONS}>;$<$:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301 target_link_options(${PROJECT_NAME} PRIVATE) +if(UNIX AND NOT APPLE AND SUNSHINE_ENABLE_STATIC_LINK) + target_link_options(${PROJECT_NAME} PRIVATE -static -static-libgcc -static-libstdc++) +endif() + if (WIN32) # prefer static libraries since we're linking statically # this fixes libcurl linking errors when using non MSYS2 version of CMake