name: CI on: push: { branches: [master] } pull_request: concurrency: # Make sure that new pushes cancel running jobs group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true env: CARGO_TERM_COLOR: always RUSTDOCFLAGS: -Dwarnings RUSTFLAGS: -Dwarnings RUST_BACKTRACE: full BENCHMARK_RUSTC: nightly-2025-05-28 # Pin the toolchain for reproducable results jobs: # Determine which tests should be run based on changed files. calculate_vars: name: Calculate workflow variables runs-on: ubuntu-24.04 timeout-minutes: 10 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} outputs: extensive_matrix: ${{ steps.script.outputs.extensive_matrix }} may_skip_libm_ci: ${{ steps.script.outputs.may_skip_libm_ci }} steps: - uses: actions/checkout@v4 with: fetch-depth: 500 - name: Fetch pull request ref run: git fetch origin "$GITHUB_REF:$GITHUB_REF" if: github.event_name == 'pull_request' - run: | set -eo pipefail # Needed to actually fail the job if ci-util fails python3 ci/ci-util.py generate-matrix | tee "$GITHUB_OUTPUT" id: script test: name: Build and test timeout-minutes: 60 strategy: fail-fast: false matrix: include: - target: aarch64-apple-darwin os: macos-15 - target: aarch64-unknown-linux-gnu os: ubuntu-24.04-arm - target: aarch64-pc-windows-msvc os: windows-2025 build_only: 1 - target: arm-unknown-linux-gnueabi os: ubuntu-24.04 - target: arm-unknown-linux-gnueabihf os: ubuntu-24.04 - target: armv7-unknown-linux-gnueabihf os: ubuntu-24.04 - target: i586-unknown-linux-gnu os: ubuntu-24.04 - target: i686-unknown-linux-gnu os: ubuntu-24.04 - target: loongarch64-unknown-linux-gnu os: ubuntu-24.04 - target: powerpc-unknown-linux-gnu os: ubuntu-24.04 - target: powerpc64-unknown-linux-gnu os: ubuntu-24.04 - target: powerpc64le-unknown-linux-gnu os: ubuntu-24.04 - target: powerpc64le-unknown-linux-gnu os: ubuntu-24.04-ppc64le - target: riscv64gc-unknown-linux-gnu os: ubuntu-24.04 - target: s390x-unknown-linux-gnu os: ubuntu-24.04-s390x - target: thumbv6m-none-eabi os: ubuntu-24.04 - target: thumbv7em-none-eabi os: ubuntu-24.04 - target: thumbv7em-none-eabihf os: ubuntu-24.04 - target: thumbv7m-none-eabi os: ubuntu-24.04 - target: wasm32-unknown-unknown os: ubuntu-24.04 - target: x86_64-unknown-linux-gnu os: ubuntu-24.04 - target: x86_64-apple-darwin os: macos-13 - target: i686-pc-windows-msvc os: windows-2025 - target: x86_64-pc-windows-msvc os: windows-2025 - target: i686-pc-windows-gnu os: windows-2025 channel: nightly-i686-gnu - target: x86_64-pc-windows-gnu os: windows-2025 channel: nightly-x86_64-gnu runs-on: ${{ matrix.os }} needs: [calculate_vars] env: BUILD_ONLY: ${{ matrix.build_only }} MAY_SKIP_LIBM_CI: ${{ needs.calculate_vars.outputs.may_skip_libm_ci }} steps: - name: Print $HOME shell: bash run: | set -x echo "${HOME:-not found}" pwd printenv - name: Print runner information run: uname -a # Native ppc and s390x runners don't have rustup by default - name: Install rustup if: matrix.os == 'ubuntu-24.04-ppc64le' || matrix.os == 'ubuntu-24.04-s390x' run: sudo apt-get update && sudo apt-get install -y rustup - uses: actions/checkout@v4 - name: Install Rust (rustup) shell: bash run: | channel="nightly" # Account for channels that have required components (MinGW) [ -n "${{ matrix.channel }}" ] && channel="${{ matrix.channel }}" rustup update "$channel" --no-self-update rustup default "$channel" rustup target add "${{ matrix.target }}" # Our scripts use nextest if possible. This is skipped on the native ppc # and s390x runners since install-action doesn't support them. - uses: taiki-e/install-action@nextest if: "!(matrix.os == 'ubuntu-24.04-ppc64le' || matrix.os == 'ubuntu-24.04-s390x')" - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.target }} - name: Cache Docker layers uses: actions/cache@v4 if: matrix.os == 'ubuntu-24.04' with: path: /tmp/.buildx-cache key: ${{ matrix.target }}-buildx-${{ github.sha }} restore-keys: ${{ matrix.target }}-buildx- # Configure buildx to use Docker layer caching - uses: docker/setup-buildx-action@v3 if: matrix.os == 'ubuntu-24.04' - name: Cache compiler-rt id: cache-compiler-rt uses: actions/cache@v4 with: path: compiler-rt key: ${{ runner.os }}-compiler-rt-${{ hashFiles('ci/download-compiler-rt.sh') }} - name: Download compiler-rt reference sources if: steps.cache-compiler-rt.outputs.cache-hit != 'true' run: ./ci/download-compiler-rt.sh shell: bash - run: echo "RUST_COMPILER_RT_ROOT=$(realpath ./compiler-rt)" >> "$GITHUB_ENV" shell: bash - name: Download musl source run: ./ci/update-musl.sh shell: bash - name: Verify API list if: matrix.os == 'ubuntu-24.04' run: python3 etc/update-api-list.py --check # Non-linux tests just use our raw script - name: Run locally if: matrix.os != 'ubuntu-24.04' shell: bash run: ./ci/run.sh ${{ matrix.target }} # Otherwise we use our docker containers to run builds - name: Run in Docker if: matrix.os == 'ubuntu-24.04' run: ./ci/run-docker.sh ${{ matrix.target }} - name: Print test logs if available if: always() run: if [ -f "target/test-log.txt" ]; then cat target/test-log.txt; fi shell: bash # Workaround to keep Docker cache smaller # https://github.com/docker/build-push-action/issues/252 # https://github.com/moby/buildkit/issues/1896 - name: Move Docker cache if: matrix.os == 'ubuntu-24.04' run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-cache clippy: name: Clippy runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - uses: actions/checkout@v4 # Unlike rustfmt, stable clippy does not work on code with nightly features. - name: Install nightly `clippy` run: | rustup set profile minimal rustup default nightly rustup component add clippy - uses: Swatinem/rust-cache@v2 - name: Download musl source run: ./ci/update-musl.sh - run: cargo clippy --workspace --all-targets build-custom: name: Build custom target runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Install Rust run: | rustup update nightly --no-self-update rustup default nightly rustup component add rust-src - uses: Swatinem/rust-cache@v2 - run: | # Ensure we can build with custom target.json files (these can interact # poorly with build scripts) cargo build -p compiler_builtins -p libm \ --target etc/thumbv7em-none-eabi-renamed.json \ -Zbuild-std=core benchmarks: name: Benchmarks timeout-minutes: 20 strategy: fail-fast: false matrix: include: - target: x86_64-unknown-linux-gnu os: ubuntu-24.04 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@master - uses: taiki-e/install-action@cargo-binstall - name: Set up dependencies run: | sudo apt-get update sudo apt-get install -y valgrind gdb libc6-dbg # Needed for iai-callgrind rustup update "$BENCHMARK_RUSTC" --no-self-update rustup default "$BENCHMARK_RUSTC" # Install the version of iai-callgrind-runner that is specified in Cargo.toml iai_version="$(cargo metadata --format-version=1 --features icount | jq -r '.packages[] | select(.name == "iai-callgrind").version')" cargo binstall -y iai-callgrind-runner --version "$iai_version" sudo apt-get install valgrind - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.target }} - name: Download musl source run: ./ci/update-musl.sh - name: Run icount benchmarks env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} run: ./ci/bench-icount.sh ${{ matrix.target }} - name: Upload the benchmark baseline uses: actions/upload-artifact@v4 with: name: ${{ env.BASELINE_NAME }} path: ${{ env.BASELINE_NAME }}.tar.xz - name: Run wall time benchmarks run: | # Always use the same seed for benchmarks. Ideally we should switch to a # non-random generator. export LIBM_SEED=benchesbenchesbenchesbencheswoo! cargo bench --package libm-test \ --no-default-features \ --features short-benchmarks,build-musl,libm/force-soft-floats - name: Print test logs if available if: always() run: if [ -f "target/test-log.txt" ]; then cat target/test-log.txt; fi shell: bash miri: name: Miri runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Install Rust (rustup) run: rustup update nightly --no-self-update && rustup default nightly shell: bash - run: rustup component add miri - run: cargo miri setup - uses: Swatinem/rust-cache@v2 - run: ./ci/miri.sh msrv: name: Check libm MSRV runs-on: ubuntu-24.04 timeout-minutes: 10 env: RUSTFLAGS: # No need to check warnings on old MSRV, unset `-Dwarnings` steps: - uses: actions/checkout@master - name: Install Rust run: | msrv="$(perl -ne 'print if s/rust-version\s*=\s*"(.*)"/\1/g' libm/Cargo.toml)" echo "MSRV: $msrv" rustup update "$msrv" --no-self-update && rustup default "$msrv" - uses: Swatinem/rust-cache@v2 - run: | # FIXME(msrv): Remove the workspace Cargo.toml so 1.63 cargo doesn't see # `edition = "2024"` and get spooked. rm Cargo.toml cargo build --manifest-path libm/Cargo.toml rustfmt: name: Rustfmt runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Install nightly `rustfmt` run: rustup set profile minimal && rustup default nightly && rustup component add rustfmt - run: cargo fmt -- --check extensive: name: Extensive tests for ${{ matrix.ty }} needs: # Wait on `clippy` so we have some confidence that the crate will build - clippy - calculate_vars runs-on: ubuntu-24.04 timeout-minutes: 240 # 4 hours strategy: matrix: # Use the output from `calculate_vars` to create the matrix # FIXME: it would be better to run all jobs (i.e. all types) but mark those that # didn't change as skipped, rather than completely excluding the job. However, # this is not currently possible https://github.com/actions/runner/issues/1985. include: ${{ fromJSON(needs.calculate_vars.outputs.extensive_matrix).extensive_matrix }} env: TO_TEST: ${{ matrix.to_test }} steps: - uses: actions/checkout@v4 - name: Install Rust run: | rustup update nightly --no-self-update rustup default nightly - uses: Swatinem/rust-cache@v2 - name: download musl source run: ./ci/update-musl.sh - name: Run extensive tests run: ./ci/run-extensive.sh - name: Print test logs if available run: if [ -f "target/test-log.txt" ]; then cat target/test-log.txt; fi shell: bash success: needs: - benchmarks - build-custom - clippy - extensive - miri - msrv - rustfmt - test runs-on: ubuntu-24.04 timeout-minutes: 10 # GitHub branch protection is exceedingly silly and treats "jobs skipped because a dependency # failed" as success. So we have to do some contortions to ensure the job fails if any of its # dependencies fails. if: always() # make sure this is never "skipped" steps: # Manually check the status of all dependencies. `if: failure()` does not work. - name: check if any dependency failed run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'