Refactor CI workflow for Ubuntu 24.04 and cross-compilation #24
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: Build and Release | |
| on: | |
| push: | |
| branches: [ main, master ] | |
| tags: [ 'v*' ] | |
| pull_request: | |
| branches: [ main, master ] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Linux — Ubuntu 24.04: native amd64 + cross-compile arm64/armhf | |
| # | |
| # Library strategy: | |
| # apt: openssl, curl, zlib, uuid, brotli, nghttp2, zstd, psl, | |
| # idn2, unistring, krb5 (all provide .a files) | |
| # source: fmt, spdlog, argon2 (small, avoids version issues) | |
| # ═══════════════════════════════════════════════════════════════════ | |
| build-linux: | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| artifact: phira-mp-server-linux-amd64 | |
| deb_arch: amd64 | |
| triple: x86_64-linux-gnu | |
| cxx: g++ | |
| cc: gcc | |
| ar: ar | |
| cross_pkg: "" | |
| is_cross: false | |
| - arch: arm64 | |
| artifact: phira-mp-server-linux-arm64 | |
| deb_arch: arm64 | |
| triple: aarch64-linux-gnu | |
| cxx: aarch64-linux-gnu-g++ | |
| cc: aarch64-linux-gnu-gcc | |
| ar: aarch64-linux-gnu-ar | |
| cross_pkg: g++-aarch64-linux-gnu | |
| is_cross: true | |
| - arch: armhf | |
| artifact: phira-mp-server-linux-armhf | |
| deb_arch: armhf | |
| triple: arm-linux-gnueabihf | |
| cxx: arm-linux-gnueabihf-g++ | |
| cc: arm-linux-gnueabihf-gcc | |
| ar: arm-linux-gnueabihf-ar | |
| cross_pkg: g++-arm-linux-gnueabihf | |
| is_cross: true | |
| env: | |
| SYSROOT: /home/runner/sysroot | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up multiarch apt (cross-compile only) | |
| if: matrix.is_cross | |
| run: | | |
| sudo dpkg --add-architecture ${{ matrix.deb_arch }} | |
| # Restrict existing sources to amd64 | |
| sudo sed -i '/^URIs:/i Architectures: amd64' /etc/apt/sources.list.d/ubuntu.sources | |
| # Add ports for cross-arch | |
| CODENAME=$(lsb_release -cs) | |
| printf 'Types: deb\nURIs: http://ports.ubuntu.com/ubuntu-ports\nSuites: %s %s-updates %s-security\nComponents: main universe\nArchitectures: %s\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n' \ | |
| "$CODENAME" "$CODENAME" "$CODENAME" "${{ matrix.deb_arch }}" \ | |
| | sudo tee /etc/apt/sources.list.d/${{ matrix.deb_arch }}-cross.sources | |
| sudo apt-get update | |
| - name: Install tools and cross-compiler | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential make pkg-config cmake ninja-build | |
| if [ -n "${{ matrix.cross_pkg }}" ]; then | |
| sudo apt-get install -y ${{ matrix.cross_pkg }} | |
| fi | |
| - name: Install library dependencies from apt | |
| run: | | |
| S="" | |
| if [ "${{ matrix.is_cross }}" = "true" ]; then S=":${{ matrix.deb_arch }}"; fi | |
| # Header-only (arch-independent) | |
| sudo apt-get install -y libboost-dev nlohmann-json3-dev | |
| # Libraries with static archives | |
| sudo apt-get install -y \ | |
| libssl-dev${S} \ | |
| libcurl4-openssl-dev${S} \ | |
| zlib1g-dev${S} \ | |
| uuid-dev${S} \ | |
| libbrotli-dev${S} \ | |
| libnghttp2-dev${S} \ | |
| libzstd-dev${S} \ | |
| libpsl-dev${S} \ | |
| libidn2-dev${S} \ | |
| libunistring-dev${S} \ | |
| libkrb5-dev${S} \ | |
| libldap-dev${S} \ | |
| libgssapi-krb5-2${S} \ | |
| librtmp-dev${S} 2>/dev/null || true | |
| # Verify key static libraries | |
| LIBDIR="/usr/lib/${{ matrix.triple }}" | |
| echo "=== Static libraries in ${LIBDIR} ===" | |
| for lib in ssl crypto curl z uuid brotlidec brotlicommon nghttp2 zstd psl idn2; do | |
| if [ -f "${LIBDIR}/lib${lib}.a" ]; then echo " OK lib${lib}.a" | |
| else echo " MISS lib${lib}.a"; fi | |
| done | |
| - name: Prepare sysroot | |
| run: mkdir -p $SYSROOT/lib/pkgconfig $SYSROOT/include | |
| - name: Build fmt from source | |
| run: | | |
| cd /tmp && git clone --depth 1 --branch 10.2.1 https://github.com/fmtlib/fmt.git && cd fmt | |
| cmake -B build -G Ninja \ | |
| -DCMAKE_C_COMPILER=${{ matrix.cc }} \ | |
| -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \ | |
| -DCMAKE_INSTALL_PREFIX=$SYSROOT \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF -DFMT_TEST=OFF -DFMT_DOC=OFF | |
| cmake --build build -j$(nproc) && cmake --install build | |
| - name: Build spdlog from source | |
| run: | | |
| cd /tmp && git clone --depth 1 --branch v1.14.1 https://github.com/gabime/spdlog.git && cd spdlog | |
| cmake -B build -G Ninja \ | |
| -DCMAKE_C_COMPILER=${{ matrix.cc }} \ | |
| -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \ | |
| -DCMAKE_INSTALL_PREFIX=$SYSROOT \ | |
| -DCMAKE_PREFIX_PATH=$SYSROOT \ | |
| -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 | |
| - name: Build argon2 from source | |
| run: | | |
| cd /tmp && git clone --depth 1 https://github.com/P-H-C/phc-winner-argon2.git && cd phc-winner-argon2 | |
| ${{ matrix.cc }} -std=c89 -O2 -c src/argon2.c -Iinclude -o argon2.o | |
| ${{ matrix.cc }} -std=c89 -O2 -c src/core.c -Iinclude -o core.o | |
| ${{ matrix.cc }} -std=c89 -O2 -c src/blake2/blake2b.c -Iinclude -o blake2b.o | |
| ${{ matrix.cc }} -std=c89 -O2 -c src/thread.c -Iinclude -o thread.o | |
| ${{ matrix.cc }} -std=c89 -O2 -c src/encoding.c -Iinclude -o encoding.o | |
| ${{ matrix.cc }} -std=c89 -O2 -c src/ref.c -Iinclude -o ref.o | |
| ${{ matrix.ar }} rcs $SYSROOT/lib/libargon2.a argon2.o core.o blake2b.o thread.o encoding.o ref.o | |
| cp include/argon2.h $SYSROOT/include/ | |
| - name: Build phira-mp-server | |
| run: | | |
| LIBDIR="/usr/lib/${{ matrix.triple }}" | |
| # Collect CFLAGS from pkg-config (include paths + defines) | |
| export PKG_CONFIG_PATH="${SYSROOT}/lib/pkgconfig:${SYSROOT}/lib64/pkgconfig:${LIBDIR}/pkgconfig:/usr/share/pkgconfig" | |
| export PKG_CONFIG_LIBDIR="${PKG_CONFIG_PATH}" | |
| PKG_CFLAGS=$(pkg-config --cflags spdlog openssl libcurl nlohmann_json 2>/dev/null || true) | |
| echo "PKG_CFLAGS: ${PKG_CFLAGS}" | |
| # Determine which static libs are available for curl's deps | |
| CURL_EXTRA="" | |
| for lib in nghttp2 zstd brotlidec brotlicommon psl idn2 unistring; do | |
| [ -f "${LIBDIR}/lib${lib}.a" ] && CURL_EXTRA="${CURL_EXTRA} -l${lib}" | |
| done | |
| # Kerberos / GSSAPI (libcurl on Ubuntu is compiled with these) | |
| for lib in gssapi_krb5 krb5 k5crypto com_err krb5support rtmp; do | |
| [ -f "${LIBDIR}/lib${lib}.a" ] && CURL_EXTRA="${CURL_EXTRA} -l${lib}" | |
| done | |
| # LDAP (try both ldap_r and ldap) | |
| if [ -f "${LIBDIR}/libldap.a" ]; then CURL_EXTRA="${CURL_EXTRA} -lldap -llber"; fi | |
| echo "CURL_EXTRA: ${CURL_EXTRA}" | |
| make -j$(nproc) \ | |
| CXX="${{ matrix.cxx }}" \ | |
| TARGET=phira-mp-server \ | |
| CXXFLAGS="-std=c++20 -Wall -Wextra -O2 -pthread \ | |
| -DLOCALES_DIR=\"./locales\" \ | |
| -DSPDLOG_FMT_EXTERNAL -DSPDLOG_COMPILED_LIB \ | |
| -I${SYSROOT}/include \ | |
| ${PKG_CFLAGS}" \ | |
| LDFLAGS="-static -L${SYSROOT}/lib -L${LIBDIR}" \ | |
| LIBS="-lspdlog -lfmt \ | |
| -lcurl -lssl -lcrypto \ | |
| -largon2 -luuid -lz \ | |
| ${CURL_EXTRA} \ | |
| -lpthread -ldl" | |
| file phira-mp-server | |
| ls -lh phira-mp-server | |
| - name: Package | |
| 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 }}/ | |
| - 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: | |
| - 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 | |
| mingw-w64-ucrt-x86_64-ntldd | |
| - name: Install or build argon2 | |
| run: | | |
| pacman -S --noconfirm mingw-w64-ucrt-x86_64-argon2 2>/dev/null && exit 0 | |
| echo ">> Building argon2 from source" | |
| 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 argon2.o | |
| gcc -std=c89 -O2 -c src/core.c -Iinclude -o core.o | |
| gcc -std=c89 -O2 -c src/blake2/blake2b.c -Iinclude -o blake2b.o | |
| gcc -std=c89 -O2 -c src/thread.c -Iinclude -o thread.o | |
| gcc -std=c89 -O2 -c src/encoding.c -Iinclude -o encoding.o | |
| gcc -std=c89 -O2 -c src/ref.c -Iinclude -o ref.o | |
| ar rcs libargon2.a argon2.o core.o blake2b.o thread.o encoding.o ref.o | |
| cp libargon2.a /ucrt64/lib/ | |
| cp include/argon2.h /ucrt64/include/ | |
| mkdir -p /ucrt64/lib/pkgconfig | |
| printf 'prefix=/ucrt64\nlibdir=${prefix}/lib\nincludedir=${prefix}/include\n\nName: libargon2\nDescription: Argon2\nVersion: 20190702\nLibs: -L${libdir} -largon2\nCflags: -I${includedir}\n' \ | |
| > /ucrt64/lib/pkgconfig/libargon2.pc | |
| - name: Build | |
| run: | | |
| make -j$(nproc) TARGET=phira-mp-server.exe LOCALES_DIR=\"./locales\" | |
| file phira-mp-server.exe | |
| - name: Collect DLLs and package | |
| run: | | |
| DEST=release/phira-mp-server-windows-amd64 | |
| mkdir -p "$DEST" | |
| cp phira-mp-server.exe "$DEST/" | |
| # ntldd outputs Windows-native paths (backslashes): | |
| # libfoo.dll => D:\a\_temp\msys64\ucrt64\bin\libfoo.dll (0x...) | |
| # Strategy: grep for 'ucrt64', take the DLL name (col 1), copy from /ucrt64/bin/ | |
| echo "=== Collecting UCRT64 DLLs ===" | |
| ntldd -R phira-mp-server.exe | grep -i 'ucrt64' | awk '{gsub(/\t/," "); print $1}' | sort -u | while IFS= read -r dllname | |
| do | |
| dllname=$(echo "$dllname" | tr -d '[:space:]') | |
| if [ -n "$dllname" ] && [ -f "/ucrt64/bin/$dllname" ]; then | |
| echo " COPY: $dllname" | |
| cp "/ucrt64/bin/$dllname" "$DEST/" | |
| fi | |
| done | |
| cp -r locales "$DEST/" | |
| echo "" | |
| echo "=== Package contents ===" | |
| ls -la "$DEST/" | |
| - name: Create zip | |
| shell: pwsh | |
| run: | | |
| Compress-Archive -Path release/phira-mp-server-windows-amd64 -DestinationPath release/phira-mp-server-windows-amd64.zip | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: phira-mp-server-windows-amd64 | |
| path: release/phira-mp-server-windows-amd64.zip | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Release | |
| # ═══════════════════════════════════════════════════════════════════ | |
| release: | |
| needs: [build-linux, build-windows] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - run: find artifacts -type f | sort | |
| - 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 |