fix #20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Makefile CI | |
| on: | |
| push: | |
| branches: [ main, master ] | |
| tags: [ 'v*' ] | |
| pull_request: | |
| branches: [ main, master ] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Linux builds — Alpine Docker for fully-static musl binaries | |
| # | |
| # Alpine does NOT ship -static packages for spdlog / fmt / argon2, | |
| # so we build them from source inside the container. | |
| # ═══════════════════════════════════════════════════════════════════ | |
| build-linux: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| platform: linux/amd64 | |
| artifact: phira-mp-server-linux-amd64 | |
| - arch: arm64 | |
| platform: linux/arm64 | |
| artifact: phira-mp-server-linux-arm64 | |
| - arch: armhf | |
| platform: linux/arm/v7 | |
| artifact: phira-mp-server-linux-armhf | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up QEMU (for cross-architecture builds) | |
| if: matrix.arch != 'amd64' | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Build static binary in Alpine container | |
| run: | | |
| docker run --rm --platform ${{ matrix.platform }} \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| -w /workspace \ | |
| alpine:3.21 \ | |
| sh -c ' | |
| set -ex | |
| # ══════════════════════════════════════════════════════ | |
| # 1. Build tools | |
| # ══════════════════════════════════════════════════════ | |
| apk add --no-cache \ | |
| build-base make pkgconf cmake linux-headers \ | |
| wget tar xz | |
| # ══════════════════════════════════════════════════════ | |
| # 2. Header-only libraries (from Alpine packages) | |
| # ══════════════════════════════════════════════════════ | |
| apk add --no-cache boost-dev nlohmann-json | |
| # ══════════════════════════════════════════════════════ | |
| # 3. Libraries that DO have -static packages in Alpine | |
| # ══════════════════════════════════════════════════════ | |
| apk add --no-cache \ | |
| openssl-dev openssl-libs-static \ | |
| curl-dev curl-static \ | |
| zlib-dev zlib-static \ | |
| brotli-dev brotli-static \ | |
| nghttp2-dev nghttp2-static | |
| # Optional transitive deps of libcurl (try each individually) | |
| for pkg in \ | |
| zstd-dev zstd-static \ | |
| libpsl-dev libpsl-static \ | |
| libidn2-dev libidn2-static \ | |
| libunistring-dev libunistring-static \ | |
| c-ares-dev c-ares-static | |
| do | |
| apk add --no-cache "$pkg" 2>/dev/null || echo "SKIP: $pkg" | |
| done | |
| # ══════════════════════════════════════════════════════ | |
| # 4. Build fmt from source (Alpine has NO fmt-static) | |
| # ══════════════════════════════════════════════════════ | |
| cd /tmp | |
| wget -q https://github.com/fmtlib/fmt/archive/refs/tags/10.2.1.tar.gz -O fmt.tar.gz | |
| tar xf fmt.tar.gz && cd fmt-10.2.1 | |
| cmake -B build \ | |
| -DCMAKE_INSTALL_PREFIX=/usr \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DFMT_TEST=OFF \ | |
| -DFMT_DOC=OFF | |
| cmake --build build -j$(nproc) | |
| cmake --install build | |
| # ══════════════════════════════════════════════════════ | |
| # 5. Build spdlog from source (Alpine has NO spdlog-static) | |
| # ══════════════════════════════════════════════════════ | |
| cd /tmp | |
| wget -q https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz -O spdlog.tar.gz | |
| tar xf spdlog.tar.gz && cd spdlog-1.14.1 | |
| cmake -B build \ | |
| -DCMAKE_INSTALL_PREFIX=/usr \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DSPDLOG_BUILD_EXAMPLE=OFF \ | |
| -DSPDLOG_BUILD_TESTS=OFF \ | |
| -DSPDLOG_FMT_EXTERNAL=ON | |
| cmake --build build -j$(nproc) | |
| cmake --install build | |
| # ══════════════════════════════════════════════════════ | |
| # 6. Build libargon2 from source (no -static package) | |
| # ══════════════════════════════════════════════════════ | |
| cd /tmp | |
| wget -q https://github.com/P-H-C/phc-winner-argon2/archive/refs/tags/20190702.tar.gz -O argon2.tar.gz | |
| tar xf argon2.tar.gz && cd phc-winner-argon2-20190702 | |
| cc -std=c89 -O2 -c src/argon2.c -Iinclude -o src/argon2.o | |
| cc -std=c89 -O2 -c src/core.c -Iinclude -o src/core.o | |
| cc -std=c89 -O2 -c src/blake2/blake2b.c -Iinclude -o src/blake2/blake2b.o | |
| cc -std=c89 -O2 -c src/thread.c -Iinclude -o src/thread.o | |
| cc -std=c89 -O2 -c src/encoding.c -Iinclude -o src/encoding.o | |
| cc -std=c89 -O2 -c src/ref.c -Iinclude -o src/ref.o | |
| ar rcs libargon2.a src/argon2.o src/core.o src/blake2/blake2b.o \ | |
| src/thread.o src/encoding.o src/ref.o | |
| cp libargon2.a /usr/lib/ | |
| cp include/argon2.h /usr/include/ | |
| cat > /usr/lib/pkgconfig/libargon2.pc << PC | |
| prefix=/usr | |
| libdir=\${prefix}/lib | |
| includedir=\${prefix}/include | |
| Name: libargon2 | |
| Description: Argon2 password hashing library | |
| Version: 20190702 | |
| Libs: -L\${libdir} -largon2 | |
| Cflags: -I\${includedir} | |
| PC | |
| # ══════════════════════════════════════════════════════ | |
| # 7. Build libuuid static if missing | |
| # ══════════════════════════════════════════════════════ | |
| apk add --no-cache util-linux-dev 2>/dev/null || true | |
| if [ ! -f /usr/lib/libuuid.a ]; then | |
| echo ">> Building libuuid.a from source" | |
| cd /tmp | |
| apk add --no-cache autoconf automake libtool gettext-dev bison | |
| wget -q https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.40/util-linux-2.40.2.tar.xz -O util-linux.tar.xz | |
| tar xf util-linux.tar.xz && cd util-linux-2.40.2 | |
| ./configure --disable-all-programs --enable-libuuid \ | |
| --enable-static --disable-shared --prefix=/usr | |
| make -j$(nproc) | |
| make install | |
| fi | |
| # ══════════════════════════════════════════════════════ | |
| # 8. Diagnostics | |
| # ══════════════════════════════════════════════════════ | |
| echo "===== Verifying static libraries =====" | |
| for lib in spdlog fmt argon2 uuid curl ssl crypto z zstd brotlidec brotlicommon nghttp2; do | |
| [ -f /usr/lib/lib${lib}.a ] && echo "OK /usr/lib/lib${lib}.a" || echo "MISS /usr/lib/lib${lib}.a" | |
| done | |
| echo "===== pkg-config --static --libs libcurl =====" | |
| pkg-config --static --libs libcurl 2>&1 || true | |
| echo "===== pkg-config --static --libs spdlog =====" | |
| pkg-config --static --libs spdlog 2>&1 || true | |
| # ══════════════════════════════════════════════════════ | |
| # 9. Build the project | |
| # ══════════════════════════════════════════════════════ | |
| cd /workspace | |
| make clean | |
| make -j$(nproc) \ | |
| STATIC=1 \ | |
| LOCALES_DIR=\"./locales\" \ | |
| TARGET=phira-mp-server | |
| # ══════════════════════════════════════════════════════ | |
| # 10. Verify | |
| # ══════════════════════════════════════════════════════ | |
| file phira-mp-server | |
| ls -lh phira-mp-server | |
| ' | |
| - name: Package artifact | |
| run: | | |
| mkdir -p release/${{ matrix.artifact }} | |
| cp phira-mp-server release/${{ matrix.artifact }}/ | |
| cp -r locales release/${{ matrix.artifact }}/ | |
| cd release | |
| tar czf ${{ matrix.artifact }}.tar.gz ${{ matrix.artifact }}/ | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: release/${{ matrix.artifact }}.tar.gz | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Windows amd64 — MSYS2 UCRT64, dynamic linking, bundle DLLs | |
| # ═══════════════════════════════════════════════════════════════════ | |
| build-windows: | |
| runs-on: windows-latest | |
| defaults: | |
| run: | |
| shell: msys2 {0} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup MSYS2 | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: UCRT64 | |
| update: true | |
| install: >- | |
| make | |
| pkgconf | |
| mingw-w64-ucrt-x86_64-gcc | |
| mingw-w64-ucrt-x86_64-boost | |
| mingw-w64-ucrt-x86_64-spdlog | |
| mingw-w64-ucrt-x86_64-fmt | |
| mingw-w64-ucrt-x86_64-nlohmann-json | |
| mingw-w64-ucrt-x86_64-curl | |
| mingw-w64-ucrt-x86_64-openssl | |
| - name: Install or build argon2 | |
| run: | | |
| # Try the MSYS2 package first | |
| pacman -S --noconfirm mingw-w64-ucrt-x86_64-argon2 2>/dev/null && { | |
| echo "argon2 installed from MSYS2 repo" | |
| exit 0 | |
| } | |
| echo ">> Building argon2 from source for MinGW" | |
| cd /tmp | |
| curl -sL https://github.com/P-H-C/phc-winner-argon2/archive/refs/tags/20190702.tar.gz | tar xz | |
| cd phc-winner-argon2-20190702 | |
| gcc -std=c89 -O2 -c src/argon2.c -Iinclude -o src/argon2.o | |
| gcc -std=c89 -O2 -c src/core.c -Iinclude -o src/core.o | |
| gcc -std=c89 -O2 -c src/blake2/blake2b.c -Iinclude -o src/blake2/blake2b.o | |
| gcc -std=c89 -O2 -c src/thread.c -Iinclude -o src/thread.o | |
| gcc -std=c89 -O2 -c src/encoding.c -Iinclude -o src/encoding.o | |
| gcc -std=c89 -O2 -c src/ref.c -Iinclude -o src/ref.o | |
| ar rcs libargon2.a src/argon2.o src/core.o src/blake2/blake2b.o \ | |
| src/thread.o src/encoding.o src/ref.o | |
| cp libargon2.a /ucrt64/lib/ | |
| cp include/argon2.h /ucrt64/include/ | |
| # Create pkg-config file so Makefile can find it | |
| mkdir -p /ucrt64/lib/pkgconfig | |
| cat > /ucrt64/lib/pkgconfig/libargon2.pc << 'PC' | |
| prefix=/ucrt64 | |
| libdir=${prefix}/lib | |
| includedir=${prefix}/include | |
| Name: libargon2 | |
| Description: Argon2 password hashing library | |
| Version: 20190702 | |
| Libs: -L${libdir} -largon2 | |
| Cflags: -I${includedir} | |
| PC | |
| - name: Build | |
| run: | | |
| echo "===== pkg-config diagnostics =====" | |
| pkg-config --libs spdlog libargon2 nlohmann_json openssl libcurl 2>&1 || true | |
| make -j$(nproc) \ | |
| TARGET=phira-mp-server.exe \ | |
| LOCALES_DIR=\"./locales\" | |
| file phira-mp-server.exe | |
| ls -lh phira-mp-server.exe | |
| - name: Collect DLLs and package | |
| run: | | |
| mkdir -p release/phira-mp-server-windows-amd64 | |
| cp phira-mp-server.exe release/phira-mp-server-windows-amd64/ | |
| # ── Recursively collect every UCRT64 DLL the exe needs ── | |
| collect_dlls() { | |
| local binary="$1" | |
| ldd "$binary" 2>/dev/null | grep -i '/ucrt64/' | awk '{print $3}' | while read -r dll; do | |
| [ -z "$dll" ] && continue | |
| local base | |
| base=$(basename "$dll") | |
| if [ ! -f "release/phira-mp-server-windows-amd64/$base" ] && [ -f "$dll" ]; then | |
| cp "$dll" release/phira-mp-server-windows-amd64/ | |
| collect_dlls "$dll" | |
| fi | |
| done | |
| } | |
| collect_dlls phira-mp-server.exe | |
| cp -r locales release/phira-mp-server-windows-amd64/ | |
| echo "===== Package contents =====" | |
| ls -la release/phira-mp-server-windows-amd64/ | |
| - name: Create zip | |
| shell: pwsh | |
| run: | | |
| Compress-Archive -Path release/phira-mp-server-windows-amd64 -DestinationPath release/phira-mp-server-windows-amd64.zip | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: phira-mp-server-windows-amd64 | |
| path: release/phira-mp-server-windows-amd64.zip | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Release — create GitHub Release when a tag is pushed | |
| # ═══════════════════════════════════════════════════════════════════ | |
| release: | |
| needs: [build-linux, build-windows] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: List artifacts | |
| run: find artifacts -type f | sort | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| artifacts/phira-mp-server-linux-amd64/*.tar.gz | |
| artifacts/phira-mp-server-linux-arm64/*.tar.gz | |
| artifacts/phira-mp-server-linux-armhf/*.tar.gz | |
| artifacts/phira-mp-server-windows-amd64/*.zip | |
| generate_release_notes: true | |
| draft: false | |
| prerelease: false |