Build precompiled NIFs #16
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 precompiled NIFs | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| # Manual build-only run for validating the workflow (incl. PGO) without | |
| # cutting a release: create/upload steps are gated on a tag, so a dispatch | |
| # builds every target but publishes nothing. | |
| workflow_dispatch: | |
| jobs: | |
| create_release: | |
| name: Create GitHub release | |
| runs-on: ubuntu-22.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Create release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release create "${{ github.ref_name }}" --repo "${{ github.repository }}" --title "${{ github.ref_name }}" --draft | |
| build_release: | |
| name: NIF ${{ matrix.nif }} - ${{ matrix.job.target }}${{ matrix.job.variant && format(' ({0})', matrix.job.variant) || '' }} | |
| needs: create_release | |
| runs-on: ${{ matrix.job.os }} | |
| permissions: | |
| contents: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| nif: ["2.15"] | |
| # pgo: true is set only where the runner can natively run the | |
| # instrumented NIF to collect a profile (runner arch == target arch). | |
| # Cross-compiled targets build plain -O3 (no native runner to profile on). | |
| job: | |
| - { target: aarch64-apple-darwin, os: macos-14, pgo: true } | |
| - { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04, use-cross: true } | |
| - { target: x86_64-apple-darwin, os: macos-14, rustflags: "-C target-cpu=x86-64" } | |
| - { target: x86_64-apple-darwin, os: macos-14, rustflags: "-C target-cpu=x86-64-v2", variant: v2 } | |
| - { target: x86_64-apple-darwin, os: macos-14, rustflags: "-C target-cpu=x86-64-v3", variant: v3 } | |
| - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04, rustflags: "-C target-cpu=x86-64", pgo: true } | |
| - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04, rustflags: "-C target-cpu=x86-64-v2", variant: v2, pgo: true } | |
| - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04, rustflags: "-C target-cpu=x86-64-v3", variant: v3, pgo: true } | |
| steps: | |
| - name: Checkout source code | |
| uses: actions/checkout@v6 | |
| - name: Extract project version | |
| shell: bash | |
| run: | | |
| echo "PROJECT_VERSION=$(sed -n 's/^ @version "\(.*\)"/\1/p' mix.exs | head -n1)" >> $GITHUB_ENV | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| target: ${{ matrix.job.target }} | |
| # llvm-tools provides the version-matched llvm-profdata used to merge | |
| # the PGO profile (no-op for non-PGO jobs). | |
| components: llvm-tools | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| prefix-key: v0-precomp | |
| shared-key: ${{ matrix.job.target }}-${{ matrix.nif }}-${{ matrix.job.variant || 'base' }} | |
| - name: Install cross | |
| if: matrix.job.use-cross | |
| shell: bash | |
| run: cargo install cross --git https://github.com/cross-rs/cross | |
| - name: Set up Elixir for PGO profiling (Linux) | |
| if: matrix.job.pgo && runner.os == 'Linux' | |
| uses: erlef/setup-beam@v1 | |
| with: | |
| otp-version: "27" | |
| elixir-version: "1.18" | |
| - name: Set up Elixir for PGO profiling (macOS) | |
| if: matrix.job.pgo && runner.os == 'macOS' | |
| shell: bash | |
| run: brew install elixir | |
| - name: Collect PGO profile | |
| if: matrix.job.pgo | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mix local.hex --force | |
| mix local.rebar --force | |
| export TORQUE_BUILD=true | |
| PGO_DIR="$PWD/pgo" | |
| rm -rf "$PGO_DIR"; mkdir -p "$PGO_DIR" | |
| mix deps.get | |
| # Build an instrumented NIF (generic CPU so it runs on this runner) and | |
| # exercise it through the BEAM to collect a same-arch profile. The | |
| # workload has no external deps, so a bare mix env suffices. | |
| RUSTFLAGS="-Cprofile-generate=$PGO_DIR" mix compile | |
| RUSTFLAGS="-Cprofile-generate=$PGO_DIR" mix run bench/pgo_workload.exs | |
| # Merge with the toolchain's version-matched llvm-profdata. | |
| HOST="$(rustc -vV | sed -n 's/host: //p')" | |
| PROFDATA="$(rustc --print sysroot)/lib/rustlib/${HOST}/bin/llvm-profdata" | |
| "$PROFDATA" merge -o "$PGO_DIR/merged.profdata" "$PGO_DIR"/*.profraw | |
| echo "PGO_FLAGS=-Cprofile-use=$PGO_DIR/merged.profdata" >> "$GITHUB_ENV" | |
| echo "::notice::Collected PGO profile for ${{ matrix.job.target }}${{ matrix.job.variant && format(' ({0})', matrix.job.variant) || '' }}" | |
| - name: Build native library | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| FLAGS="${{ matrix.job.rustflags }}" | |
| if [ -n "${PGO_FLAGS:-}" ]; then | |
| FLAGS="$FLAGS ${PGO_FLAGS}" | |
| echo "Building ${{ matrix.job.target }} with PGO" | |
| else | |
| echo "Building ${{ matrix.job.target }} without PGO" | |
| fi | |
| if [ -n "$FLAGS" ]; then export RUSTFLAGS="$FLAGS"; fi | |
| if [ "${{ matrix.job.use-cross }}" = "true" ]; then | |
| RUSTLER_NIF_VERSION=${{ matrix.nif }} cross build --release --target ${{ matrix.job.target }} --package torque_nif | |
| else | |
| RUSTLER_NIF_VERSION=${{ matrix.nif }} cargo build --release --target ${{ matrix.job.target }} --package torque_nif | |
| fi | |
| - name: Package artifact | |
| shell: bash | |
| run: | | |
| TARGET=${{ matrix.job.target }} | |
| VERSION=${{ env.PROJECT_VERSION }} | |
| NIF=${{ matrix.nif }} | |
| if [[ "$TARGET" == *"darwin"* ]]; then | |
| LIB_FILE="target/${TARGET}/release/libtorque_nif.dylib" | |
| else | |
| LIB_FILE="target/${TARGET}/release/libtorque_nif.so" | |
| fi | |
| # RustlerPrecompiled expects the file inside the archive to match this name | |
| VARIANT="${{ matrix.job.variant }}" | |
| NIF_NAME="libtorque_nif-v${VERSION}-nif-${NIF}-${TARGET}" | |
| if [ -n "${VARIANT}" ]; then | |
| NIF_NAME="${NIF_NAME}--${VARIANT}" | |
| fi | |
| NIF_NAME="${NIF_NAME}.so" | |
| cp "${LIB_FILE}" "${NIF_NAME}" | |
| ARCHIVE_NAME="${NIF_NAME}.tar.gz" | |
| tar -czf "${ARCHIVE_NAME}" "${NIF_NAME}" | |
| echo "ARCHIVE_NAME=${ARCHIVE_NAME}" >> $GITHUB_ENV | |
| - name: Upload to GitHub release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release upload "${{ github.ref_name }}" "${{ env.ARCHIVE_NAME }}" --clobber |