From 6423c8e496fe2e8d37e02b9a2d0472f7f4d6f5c0 Mon Sep 17 00:00:00 2001 From: Max Lv Date: Sun, 8 Feb 2026 11:16:58 +0800 Subject: [PATCH] Add Debian package build test and statically link bundled submodules Add tests/test_deb_build.sh with four phases: 1. Build .deb packages via dpkg-buildpackage 2. Verify all three packages contain expected files (binaries, man pages, shared library, headers, pkg-config) 3. Install packages and resolve dependencies 4. Smoke-test installed binaries, ldconfig, and headers Add a Linux-only CI step in build.yml to run the test. Statically link the bundled submodule libraries (libcork, libipset, libbloom) into all targets instead of building them as shared libs. These are vendored code built from git submodules, not system libraries, so static linking is correct and eliminates dpkg-shlibdeps warnings about missing .so files. Pass -DWITH_STATIC=OFF in debian/rules since debian build-depends only provide shared library dev packages. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build.yml | 8 ++ CMakeLists.txt | 19 +--- debian/rules | 2 +- src/CMakeLists.txt | 6 +- tests/CMakeLists.txt | 10 +- tests/test_deb_build.sh | 190 ++++++++++++++++++++++++++++++++++++ 6 files changed, 208 insertions(+), 27 deletions(-) create mode 100644 tests/test_deb_build.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8623863a..9c905a6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,3 +52,11 @@ jobs: sudo apt-get install -y qemu-system-x86 bash tests/test_redir_qemu.sh build/shared/bin/ timeout-minutes: 8 + + - name: Debian package build test + if: runner.os == 'Linux' + run: | + sudo apt-get install -y --no-install-recommends \ + build-essential debhelper dpkg-dev fakeroot asciidoc-base xmlto pkg-config + bash tests/test_deb_build.sh + timeout-minutes: 10 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a04623c..3616039b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,16 +137,10 @@ else () set(LIBCORK_SOURCE ${LIBCORK_SOURCE} libcork/src/libcork/posix/mingw.c) endif () -if (WITH_STATIC) add_library(cork STATIC ${LIBCORK_SOURCE}) target_compile_definitions(cork PUBLIC -DCORK_API=CORK_LOCAL) -endif () - -add_library(cork-shared SHARED ${LIBCORK_SOURCE}) -target_compile_definitions(cork-shared PUBLIC -DCORK_API=CORK_EXPORT) -set_target_properties(cork-shared PROPERTIES OUTPUT_NAME cork) if (MINGW) -target_link_libraries(cork-shared ws2_32) +target_link_libraries(cork ws2_32) endif () set(LIBIPSET_SOURCE @@ -171,28 +165,17 @@ set(LIBIPSET_SOURCE libipset/src/libipset/set/storage.c ) -if (WITH_STATIC) add_library(ipset STATIC ${LIBIPSET_SOURCE}) -endif () - -add_library(ipset-shared SHARED ${LIBIPSET_SOURCE}) -set_target_properties(ipset-shared PROPERTIES OUTPUT_NAME ipset) set(LIBBLOOM_SOURCE libbloom/bloom.c libbloom/murmur2/MurmurHash2.c ) -if (WITH_STATIC) add_library(bloom STATIC ${LIBBLOOM_SOURCE}) target_link_libraries(ipset cork bloom) endif () -add_library(bloom-shared SHARED ${LIBBLOOM_SOURCE}) -target_link_libraries(ipset-shared cork-shared bloom-shared) -set_target_properties(bloom-shared PROPERTIES OUTPUT_NAME bloom) -endif () - add_subdirectory(src) add_subdirectory(doc) diff --git a/debian/rules b/debian/rules index 5d8dae81..2121df06 100755 --- a/debian/rules +++ b/debian/rules @@ -11,7 +11,7 @@ override_dh_auto_install: dh_auto_install override_dh_auto_configure: - dh_auto_configure + dh_auto_configure -- -DWITH_STATIC=OFF override_dh_installchangelogs: dh_installchangelogs -XChanges diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 18ae7ba0..80503cb2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,9 +138,9 @@ find_library(LIBPCRE2_SHARED pcre2-8) if (WITH_EMBEDDED_SRC) list(APPEND DEPS_SHARED m - bloom-shared - cork-shared - ipset-shared + bloom + cork + ipset ${LIBEV_SHARED} ${LIBUDNS_SHARED} ${LIBPCRE2_SHARED} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b27effaf..436dc99a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,7 +43,7 @@ set(TEST_NETUTILS_LIBS ${LIBUDNS_SHARED} ) if(WITH_EMBEDDED_SRC) - list(APPEND TEST_NETUTILS_LIBS cork-shared) + list(APPEND TEST_NETUTILS_LIBS cork) else() list(APPEND TEST_NETUTILS_LIBS ${LIBCORK_SHARED}) endif() @@ -59,7 +59,7 @@ ss_add_test(test_cache # test_ppbloom - needs bloom set(TEST_PPBLOOM_LIBS "") if(WITH_EMBEDDED_SRC) - list(APPEND TEST_PPBLOOM_LIBS bloom-shared) + list(APPEND TEST_PPBLOOM_LIBS bloom) else() list(APPEND TEST_PPBLOOM_LIBS ${LIBBLOOM_SHARED}) endif() @@ -73,7 +73,7 @@ set(TEST_RULE_LIBS ${LIBSODIUM_SHARED} ) if(WITH_EMBEDDED_SRC) - list(APPEND TEST_RULE_LIBS cork-shared) + list(APPEND TEST_RULE_LIBS cork) else() list(APPEND TEST_RULE_LIBS ${LIBCORK_SHARED}) endif() @@ -88,7 +88,7 @@ set(TEST_JCONF_LIBS ${LIBUDNS_SHARED} ) if(WITH_EMBEDDED_SRC) - list(APPEND TEST_JCONF_LIBS cork-shared) + list(APPEND TEST_JCONF_LIBS cork) else() list(APPEND TEST_JCONF_LIBS ${LIBCORK_SHARED}) endif() @@ -109,7 +109,7 @@ set(TEST_CRYPTO_LIBS ${LIBEV_SHARED} ) if(WITH_EMBEDDED_SRC) - list(APPEND TEST_CRYPTO_LIBS bloom-shared) + list(APPEND TEST_CRYPTO_LIBS bloom) else() list(APPEND TEST_CRYPTO_LIBS ${LIBBLOOM_SHARED}) endif() diff --git a/tests/test_deb_build.sh b/tests/test_deb_build.sh new file mode 100644 index 00000000..7dbca66d --- /dev/null +++ b/tests/test_deb_build.sh @@ -0,0 +1,190 @@ +#!/bin/bash +# +# Test that Debian packages build, contain expected files, install, and work. +# +# Usage: bash tests/test_deb_build.sh +# +# Requirements (Linux only): +# - dpkg-buildpackage, debhelper, fakeroot +# - Build dependencies listed in debian/control +# - sudo (for install phase) +# + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +# --- helpers ---------------------------------------------------------------- + +PASS_COUNT=0 +FAIL_COUNT=0 +FAILURES="" + +log() { + echo "=== [test_deb_build] $* ===" +} + +check_pass() { + PASS_COUNT=$((PASS_COUNT + 1)) + echo " PASS: $*" +} + +check_fail() { + FAIL_COUNT=$((FAIL_COUNT + 1)) + FAILURES="${FAILURES} FAIL: $*"$'\n' + echo " FAIL: $*" +} + +# check_file_in_deb DEB_FILE PATTERN DESCRIPTION +# Verifies that at least one file matching PATTERN exists in the .deb +check_file_in_deb() { + local deb="$1" pattern="$2" desc="$3" + # Avoid grep -q: with pipefail, early grep exit causes SIGPIPE on dpkg-deb + if dpkg-deb -c "$deb" | grep -E "$pattern" >/dev/null; then + check_pass "$desc" + else + check_fail "$desc" + fi +} + +# check_command CMD DESCRIPTION +# Verifies that a command runs successfully (exit 0) +check_command() { + local desc="$2" + if eval "$1" >/dev/null 2>&1; then + check_pass "$desc" + else + check_fail "$desc" + fi +} + +summary() { + echo "" + log "Results: $PASS_COUNT passed, $FAIL_COUNT failed" + if [ "$FAIL_COUNT" -gt 0 ]; then + echo "" + echo "Failures:" + printf '%s' "$FAILURES" + exit 1 + fi +} + +# --- phase 1: build -------------------------------------------------------- + +log "Phase 1: Build Debian packages" + +cd "$PROJECT_DIR" + +# dpkg-buildpackage -b builds binary-only packages (no .orig.tar.gz needed) +dpkg-buildpackage -b -us -uc -j"$(nproc)" + +# .deb files are placed in the parent directory +PARENT_DIR="$(dirname "$PROJECT_DIR")" + +DEB_MAIN="" +DEB_LIB="" +DEB_DEV="" +for f in "$PARENT_DIR"/shadowsocks-libev_*.deb; do + [ -f "$f" ] && DEB_MAIN="$f" && break +done +for f in "$PARENT_DIR"/libshadowsocks-libev2_*.deb; do + [ -f "$f" ] && DEB_LIB="$f" && break +done +for f in "$PARENT_DIR"/libshadowsocks-libev-dev_*.deb; do + [ -f "$f" ] && DEB_DEV="$f" && break +done + +# --- phase 2: verify contents ---------------------------------------------- + +log "Phase 2: Verify package contents" + +# Check all three .deb files exist +if [ -n "$DEB_MAIN" ] && [ -f "$DEB_MAIN" ]; then + check_pass "shadowsocks-libev .deb exists: $(basename "$DEB_MAIN")" +else + check_fail "shadowsocks-libev .deb not found" +fi + +if [ -n "$DEB_LIB" ] && [ -f "$DEB_LIB" ]; then + check_pass "libshadowsocks-libev2 .deb exists: $(basename "$DEB_LIB")" +else + check_fail "libshadowsocks-libev2 .deb not found" +fi + +if [ -n "$DEB_DEV" ] && [ -f "$DEB_DEV" ]; then + check_pass "libshadowsocks-libev-dev .deb exists: $(basename "$DEB_DEV")" +else + check_fail "libshadowsocks-libev-dev .deb not found" +fi + +# Bail early if any .deb is missing - remaining checks would all fail +if [ "$FAIL_COUNT" -gt 0 ]; then + summary +fi + +# Main package: binaries +for bin in ss-local ss-server ss-redir ss-tunnel ss-manager; do + check_file_in_deb "$DEB_MAIN" "usr/bin/${bin}" "$bin binary in main package" +done + +# Main package: man pages +for bin in ss-local ss-server ss-redir ss-tunnel ss-manager; do + check_file_in_deb "$DEB_MAIN" "usr/share/man/man1/$bin\\.1" "$bin man page in main package" +done + +# Shared library package +check_file_in_deb "$DEB_LIB" "usr/lib/.*/libshadowsocks-libev\\.so\\." "shared library in lib package" + +# Dev package: header +check_file_in_deb "$DEB_DEV" "usr/include/shadowsocks\\.h" "shadowsocks.h header in dev package" + +# Dev package: pkg-config +check_file_in_deb "$DEB_DEV" "usr/lib/.*/pkgconfig/shadowsocks-libev\\.pc" "pkg-config file in dev package" + +# Dev package: unversioned .so symlink +check_file_in_deb "$DEB_DEV" "usr/lib/.*/libshadowsocks-libev\\.so[^.]" "unversioned .so symlink in dev package" + +# --- phase 3: install ------------------------------------------------------- + +log "Phase 3: Install packages" + +sudo dpkg -i "$DEB_LIB" "$DEB_DEV" "$DEB_MAIN" || true +sudo apt-get -f install -y + +# Verify dpkg thinks they are installed +for pkg in shadowsocks-libev libshadowsocks-libev2 libshadowsocks-libev-dev; do + if dpkg -s "$pkg" >/dev/null 2>&1; then + check_pass "$pkg installed" + else + check_fail "$pkg not installed" + fi +done + +# --- phase 4: smoke-test ---------------------------------------------------- + +log "Phase 4: Smoke-test installed binaries" + +# Each binary should respond to --help +for bin in ss-local ss-server ss-redir ss-tunnel ss-manager; do + check_command "$bin --help" "$bin --help runs" +done + +# Shared library should be findable by ldconfig +sudo ldconfig +if ldconfig -p | grep -q libshadowsocks-libev; then + check_pass "libshadowsocks-libev found by ldconfig" +else + check_fail "libshadowsocks-libev not found by ldconfig" +fi + +# Header should be in the include path +if [ -f /usr/include/shadowsocks.h ]; then + check_pass "shadowsocks.h installed in /usr/include" +else + check_fail "shadowsocks.h not installed in /usr/include" +fi + +# --- summary ---------------------------------------------------------------- + +summary