name: portability on: push: branches: [master] pull_request: branches: [master] workflow_dispatch: jobs: bundled: strategy: fail-fast: false matrix: os: [ubuntu-24.04, ubuntu-24.04-arm, macos-latest] minimal: ['OFF', 'ON'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 - name: Build without installed third-party development packages run: | cmake -S . -B build -DSS_MINIMAL=${{ matrix.minimal }} cmake --build build --parallel 4 ctest --test-dir build -L 'unit|vendor' --output-on-failure --no-tests=error - name: Real TCP and UDP relay run: python3 tests/interop.py --self --bin build/bin - name: Asynchronous hostname ACL bypass run: python3 tests/async_dns_interop.py --bin build/bin - name: SIP003 transport and child cleanup if: matrix.minimal == 'OFF' run: python3 tests/interop.py --self --bin build/bin --plugin tests/sip003_fixture.py - name: Relocated installation and public API consumers run: | cmake --install build --prefix "$PWD/install" mv install relocated cmake -S tests/consumer -B consumer -DCMAKE_PREFIX_PATH="$PWD/relocated" cmake --build consumer ./consumer/consumer_static ./consumer/consumer_shared cmake -S tests/consumer -B consumer-legacy -DSS_PACKAGE_NAME=shadowsocks-libev -DCMAKE_PREFIX_PATH="$PWD/relocated" cmake --build consumer-legacy ./consumer-legacy/consumer_static ./consumer-legacy/consumer_shared windows: runs-on: windows-latest defaults: run: shell: msys2 {0} steps: - uses: actions/checkout@v6 - uses: msys2/setup-msys2@v2 with: msystem: UCRT64 update: true install: >- mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-python - name: Build and run native Windows binaries run: | cmake -S . -B build -G Ninja cmake --build build --parallel 4 ctest --test-dir build -L 'unit|vendor' --output-on-failure --no-tests=error objdump -p build/bin/ss-server.exe | sed -n '/DLL Name/p' python tests/interop.py --self --bin build/bin --isolate-windows-runtime python tests/async_dns_interop.py --bin build/bin --isolate-windows-runtime - name: Installed consumers run: | cmake --install build --prefix "$PWD/install" cmake -S tests/consumer -B consumer -G Ninja -DCMAKE_PREFIX_PATH="$PWD/install" cmake --build consumer cmake -S tests/consumer -B consumer-legacy -G Ninja -DSS_PACKAGE_NAME=shadowsocks-libev -DCMAKE_PREFIX_PATH="$PWD/install" cmake --build consumer-legacy - name: Run installed consumers without toolchain DLLs shell: pwsh run: | $env:PATH = "$env:GITHUB_WORKSPACE\install\bin;$env:SystemRoot\System32;$env:SystemRoot" & ./consumer/consumer_static.exe if ($LASTEXITCODE -ne 0) { throw "Static consumer failed: $LASTEXITCODE" } & ./consumer/consumer_shared.exe if ($LASTEXITCODE -ne 0) { throw "Shared consumer failed: $LASTEXITCODE" } & ./consumer-legacy/consumer_static.exe if ($LASTEXITCODE -ne 0) { throw "Legacy static consumer failed: $LASTEXITCODE" } & ./consumer-legacy/consumer_shared.exe if ($LASTEXITCODE -ne 0) { throw "Legacy shared consumer failed: $LASTEXITCODE" } static-macos: runs-on: macos-latest env: CC: clang steps: - uses: actions/checkout@v6 - name: Build Clang static-only installation run: | cmake -S . -B build-static -DCMAKE_C_COMPILER=clang -DSS_DEPENDENCY_MODE=bundled -DWITH_STATIC=ON -DSS_BUILD_STATIC_LIBRARY=ON -DSS_BUILD_SHARED_LIBRARY=OFF cmake --build build-static --parallel 4 ctest --test-dir build-static -L 'unit|vendor' --output-on-failure --no-tests=error cmake --install build-static --prefix "$PWD/install-static" mv install-static relocated-static cmake -S tests/consumer -B consumer-static -DCMAKE_C_COMPILER=clang -DCMAKE_PREFIX_PATH="$PWD/relocated-static" cmake --build consumer-static test ! -e consumer-static/consumer_shared ./consumer-static/consumer_static - name: Reject third-party dynamic libraries run: python3 tests/check_static_runtime.py relocated-static/bin/ss-* consumer-static/consumer_static - name: Exercise installed static programs run: | python3 tests/interop.py --self --bin relocated-static/bin python3 tests/async_dns_interop.py --bin relocated-static/bin python3 tests/interop.py --self --bin relocated-static/bin --plugin tests/sip003_fixture.py - uses: actions/upload-artifact@v4 with: name: static-macos-${{ runner.arch }} path: relocated-static/ if-no-files-found: error static-windows: runs-on: windows-latest defaults: run: shell: msys2 {0} steps: - uses: actions/checkout@v6 - uses: msys2/setup-msys2@v2 with: msystem: CLANG64 update: true install: >- mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-llvm mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-ninja mingw-w64-clang-x86_64-python - name: Build Clang static-only installation run: | cmake -S . -B build-static -G Ninja -DCMAKE_C_COMPILER=clang -DSS_DEPENDENCY_MODE=bundled -DWITH_STATIC=ON -DSS_BUILD_STATIC_LIBRARY=ON -DSS_BUILD_SHARED_LIBRARY=OFF cmake --build build-static --parallel 4 ctest --test-dir build-static -L 'unit|vendor' --output-on-failure --no-tests=error cmake --install build-static --prefix "$PWD/install-static" mv install-static relocated-static cmake -S tests/consumer -B consumer-static -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_PREFIX_PATH="$PWD/relocated-static" cmake --build consumer-static test ! -e consumer-static/consumer_shared.exe - name: Reject third-party DLL imports, including delay imports run: python tests/check_static_runtime.py relocated-static/bin/ss-*.exe consumer-static/consumer_static.exe - name: Exercise installed programs without toolchain DLLs run: | python tests/interop.py --self --bin relocated-static/bin --isolate-windows-runtime python tests/async_dns_interop.py --bin relocated-static/bin --isolate-windows-runtime - name: Run static consumer with only Windows system directories on PATH shell: pwsh run: | $env:PATH = "$env:SystemRoot\System32;$env:SystemRoot" & ./consumer-static/consumer_static.exe if ($LASTEXITCODE -ne 0) { throw "Static consumer failed: $LASTEXITCODE" } - uses: actions/upload-artifact@v4 with: name: static-windows-${{ runner.arch }} path: relocated-static/ if-no-files-found: error static-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Build and test fully static Clang/musl executables run: docker build -f docker/static/Dockerfile --target artifacts --output type=local,dest=static-linux . - uses: actions/upload-artifact@v4 with: name: static-linux-${{ runner.arch }} path: static-linux/ if-no-files-found: error freebsd: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: vmactions/freebsd-vm@v1 with: release: '14.3' usesh: true prepare: pkg install -y cmake ninja python3 run: | cmake -S . -B build -G Ninja cmake --build build --parallel 4 ctest --test-dir build -L 'unit|vendor' --output-on-failure --no-tests=error python3 tests/interop.py --self --bin build/bin python3 tests/async_dns_interop.py --bin build/bin cmake --install build --prefix "$PWD/install" cmake -S tests/consumer -B consumer -G Ninja -DCMAKE_PREFIX_PATH="$PWD/install" cmake --build consumer ./consumer/consumer_static ./consumer/consumer_shared offline-musl: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Source archive run: python3 scripts/source_archive.py release/shadowsocks-c.tar.gz - name: Prepare compiler image run: | printf 'FROM alpine:3.24\nRUN apk add --no-cache build-base cmake python3 linux-headers\n' > /tmp/ss-toolchain.Dockerfile docker build -t ss-toolchain -f /tmp/ss-toolchain.Dockerfile /tmp - name: Build release archive with network disabled run: | docker run --rm --network none -v "$PWD/release:/release:ro" ss-toolchain sh -ec ' mkdir /src tar xzf /release/shadowsocks-c.tar.gz -C /src --strip-components=1 cmake -S /src -B /build -DCMAKE_DISABLE_FIND_PACKAGE_Python3=TRUE cmake --build /build --parallel 4 ctest --test-dir /build -L "unit|vendor" --output-on-failure --no-tests=error python3 /src/tests/interop.py --self --bin /build/bin '