| name: CI | |
| permissions: {} | |
| on: | |
| push: { branches: [main] } | |
| 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 | |
| LIBM_BUILD_VERBOSE: true | |
| RUSTDOCFLAGS: -Dwarnings | |
| RUSTFLAGS: -Dwarnings | |
| RUST_BACKTRACE: full | |
| BENCHMARK_RUSTC: nightly-2026-02-10 # 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| 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 | |
| # NOTE: self-hosted riscv64 runners are experimental and may be flaky. | |
| # Do not block CI on failures from this platform for now. | |
| continue-on-error: ${{ contains(matrix.os, 'self-hosted') }} | |
| 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-11-arm | |
| - 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 | |
| # FIXME(ci): re-enable these once more capacity is avialable | |
| # - target: riscv64gc-unknown-linux-gnu | |
| # os: ["self-hosted", "linux", "riscv64"] | |
| - 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-15-intel | |
| - 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 }} | |
| JOB_TARGET: ${{ matrix.target }} | |
| JOB_CHANNEL: ${{ matrix.channel }} | |
| MAY_SKIP_LIBM_CI: ${{ needs.calculate_vars.outputs.may_skip_libm_ci }} | |
| RUN_IN_DOCKER: ${{ matrix.os == 'ubuntu-24.04' }} | |
| steps: | |
| - name: Print runner information | |
| shell: bash | |
| run: | | |
| set -x | |
| uname -a | |
| lscpu || (sysctl -a | grep cpu) || true | |
| echo "home: ${HOME:-not found}" | |
| pwd | |
| # 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - name: Install Rust (rustup) | |
| shell: bash | |
| run: | | |
| channel="nightly" | |
| # Account for channels that have required components (MinGW) | |
| [ -n "$JOB_CHANNEL" ] && channel="$JOB_CHANNEL" | |
| rustup update "$channel" --no-self-update | |
| rustup default "$channel" | |
| rustup target add "$JOB_TARGET" | |
| - uses: taiki-e/install-action@920ab1831fbf4fb3ef75c8ead83556c918bb7290 # v2.79.8 | |
| with: | |
| tool: nextest@0.9.131 | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| with: | |
| key: ${{ matrix.target }} | |
| - name: Cache Docker layers | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| if: ${{ env.RUN_IN_DOCKER == 'true' }} | |
| 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@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 | |
| if: ${{ env.RUN_IN_DOCKER == 'true' }} | |
| - name: Cache compiler-rt | |
| id: cache-compiler-rt | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| 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' || contains(matrix.os, 'self-hosted') | |
| run: | | |
| # Must be run on the host (not in Docker) because git and fs access is required. | |
| python3 etc/update-api-list.py --check | |
| cargo test -p update-api-list | |
| # Non-linux tests just use our raw script | |
| - name: Run locally | |
| if: ${{ env.RUN_IN_DOCKER != 'true' }} | |
| shell: bash | |
| run: ./ci/run.sh "$JOB_TARGET" | |
| # Otherwise we use our docker containers to run builds | |
| - name: Run in Docker | |
| if: ${{ env.RUN_IN_DOCKER == 'true' }} | |
| run: ./ci/run-docker.sh "$JOB_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: ${{ env.RUN_IN_DOCKER == 'true' }} | |
| 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| # Unlike rustfmt, stable clippy does not work on code with nightly features. | |
| - name: Install nightly `clippy` | |
| run: | | |
| rustup update nightly --no-self-update | |
| rustup default nightly | |
| rustup component add clippy | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| - name: Download musl source | |
| run: ./ci/update-musl.sh | |
| - run: cargo clippy --workspace --all-targets | |
| zizmor: | |
| name: Zizmor (Static analysis for GitHub Actions) | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| security-events: write | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 | |
| build-custom: | |
| name: Build custom target | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - name: Install Rust | |
| run: | | |
| rustup update nightly --no-self-update | |
| rustup default nightly | |
| rustup component add rust-src | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| - 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 \ | |
| -Zjson-target-spec | |
| # FIXME: move this target to test job once https://github.com/rust-lang/rust/pull/150138 merged. | |
| build-thumbv6k: | |
| name: Build thumbv6k | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - name: Install Rust | |
| run: | | |
| rustup update nightly --no-self-update | |
| rustup default nightly | |
| rustup component add rust-src | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| - run: | | |
| cargo build -p compiler_builtins -p libm \ | |
| --target etc/thumbv6-none-eabi.json \ | |
| -Zbuild-std=core \ | |
| -Zjson-target-spec | |
| benchmarks: | |
| name: Benchmarks | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-24.04-arm | |
| - target: i686-unknown-linux-gnu | |
| os: ubuntu-24.04 | |
| - target: x86_64-unknown-linux-gnu | |
| os: ubuntu-24.04 | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| JOB_TARGET: ${{ matrix.target }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - uses: taiki-e/install-action@920ab1831fbf4fb3ef75c8ead83556c918bb7290 # v2.79.8 | |
| with: | |
| tool: cargo-binstall@1.17.7 | |
| - name: Set up dependencies | |
| run: ./ci/install-bench-deps.sh "$JOB_TARGET" | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| 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 "$JOB_TARGET" | |
| - name: Upload the benchmark baseline | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: ${{ env.BASELINE_NAME }} | |
| path: ${{ env.BASELINE_NAME }}.tar.xz | |
| - 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - 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@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| - 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - 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@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| - 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: { persist-credentials: false } | |
| - name: Install Rust | |
| run: | | |
| rustup update nightly --no-self-update | |
| rustup default nightly | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| - 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 | |
| - build-thumbv6k | |
| - clippy | |
| - extensive | |
| - miri | |
| - msrv | |
| - rustfmt | |
| - test | |
| - zizmor | |
| 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" | |
| env: | |
| NEEDS: ${{ toJson(needs) }} | |
| 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")' <<< "$NEEDS" |