ci: Skip testing libm in PRs if it did not change

Many contributions to compiler-builtins don't have any need to touch
libm, and could get by with the few minutes of CI for compiler-builtins
rather than the ~30 minutes for libm. We already have some scripts that
handle changed file detection, so expand its use to skip libm CI if it
doesn't need to run.
This commit is contained in:
Trevor Gross 2025-04-20 07:05:32 +00:00 committed by Trevor Gross
parent a829d916b5
commit 8902f740da
4 changed files with 66 additions and 37 deletions

View file

@ -16,6 +16,27 @@ env:
BENCHMARK_RUSTC: nightly-2025-01-16 # 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: python3 ci/ci-util.py generate-matrix >> "$GITHUB_OUTPUT"
id: script
test:
name: Build and test
timeout-minutes: 60
@ -78,9 +99,11 @@ jobs:
os: windows-2025
channel: nightly-x86_64-gnu
runs-on: ${{ matrix.os }}
needs: [calculate_vars]
env:
BUILD_ONLY: ${{ matrix.build_only }}
TEST_VERBATIM: ${{ matrix.test_verbatim }}
MAY_SKIP_LIBM_CI: ${{ needs.calculate_vars.outputs.may_skip_libm_ci }}
steps:
- name: Print runner information
run: uname -a
@ -267,41 +290,21 @@ jobs:
run: rustup set profile minimal && rustup default stable && rustup component add rustfmt
- run: cargo fmt -- --check
# Determine which extensive tests should be run based on changed files.
calculate_extensive_matrix:
name: Calculate job matrix
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
outputs:
matrix: ${{ steps.script.outputs.matrix }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 100
- name: Fetch pull request ref
run: git fetch origin "$GITHUB_REF:$GITHUB_REF"
if: github.event_name == 'pull_request'
- run: python3 ci/ci-util.py generate-matrix >> "$GITHUB_OUTPUT"
id: script
extensive:
name: Extensive tests for ${{ matrix.ty }}
needs:
# Wait on `clippy` so we have some confidence that the crate will build
- clippy
- calculate_extensive_matrix
- calculate_vars
runs-on: ubuntu-24.04
timeout-minutes: 240 # 4 hours
strategy:
matrix:
# Use the output from `calculate_extensive_matrix` to calculate the 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_extensive_matrix.outputs.matrix).matrix }}
include: ${{ fromJSON(needs.calculate_vars.outputs.extensive_matrix).extensive_matrix }}
env:
TO_TEST: ${{ matrix.to_test }}
steps:

View file

@ -7,6 +7,7 @@ git history.
import json
import os
import re
import subprocess as sp
import sys
from dataclasses import dataclass
@ -68,6 +69,10 @@ IGNORE_FILES = [
"libm/src/math/arch/intrinsics.rs",
]
# libm PR CI takes a long time and doesn't need to run unless relevant files have been
# changed. Anything matching this regex pattern will trigger a run.
TRIGGER_LIBM_PR_CI = ".*(libm|musl).*"
TYPES = ["f16", "f32", "f64", "f128"]
@ -116,7 +121,6 @@ class FunctionDef(TypedDict):
type: str
@dataclass
class Context:
gh_ref: str | None
changed: list[Path]
@ -142,7 +146,7 @@ class Context:
# the PR number), and sets this as `GITHUB_REF`.
ref = self.gh_ref
eprint(f"using ref `{ref}`")
if ref is None or "merge" not in ref:
if not self.is_pr():
# If the ref is not for `merge` then we are not in PR CI
eprint("No diff available for ref")
return
@ -170,6 +174,10 @@ class Context:
)
self.changed = [Path(p) for p in textlist.splitlines()]
def is_pr(self) -> bool:
"""Check if we are looking at a PR rather than a push."""
return self.gh_ref is not None and "merge" in self.gh_ref
@staticmethod
def _ignore_file(fname: str) -> bool:
return any(fname.startswith(pfx) for pfx in IGNORE_FILES)
@ -196,7 +204,16 @@ class Context:
return ret
def make_workflow_output(self) -> str:
def may_skip_libm_ci(self) -> bool:
"""If this is a PR and no libm files were changed, allow skipping libm
jobs."""
if self.is_pr():
return all(not re.match(TRIGGER_LIBM_PR_CI, str(f)) for f in self.changed)
return False
def emit_workflow_output(self):
"""Create a JSON object a list items for each type's changed files, if any
did change, and the routines that were affected by the change.
"""
@ -216,9 +233,10 @@ class Context:
eprint("Skipping all extensive tests")
changed = self.changed_routines()
ret = []
matrix = []
total_to_test = 0
# Figure out which extensive tests need to run
for ty in TYPES:
ty_changed = changed.get(ty, [])
ty_to_test = [] if skip_tests else ty_changed
@ -230,9 +248,14 @@ class Context:
"to_test": ",".join(ty_to_test),
}
ret.append(item)
output = json.dumps({"matrix": ret}, separators=(",", ":"))
eprint(f"output: {output}")
matrix.append(item)
ext_matrix = json.dumps({"extensive_matrix": matrix}, separators=(",", ":"))
may_skip = str(self.may_skip_libm_ci()).lower()
print(f"extensive_matrix={ext_matrix}")
print(f"may_skip_libm_ci={may_skip}")
eprint(f"extensive_matrix={ext_matrix}")
eprint(f"may_skip_libm_ci={may_skip}")
eprint(f"total extensive tests: {total_to_test}")
if error_on_many_tests and total_to_test > MANY_EXTENSIVE_THRESHOLD:
@ -242,8 +265,6 @@ class Context:
)
exit(1)
return output
def locate_baseline(flags: list[str]) -> None:
"""Find the most recent baseline from CI, download it if specified.
@ -398,8 +419,7 @@ def main():
match sys.argv[1:]:
case ["generate-matrix"]:
ctx = Context()
output = ctx.make_workflow_output()
print(f"matrix={output}")
ctx.emit_workflow_output()
case ["locate-baseline", *flags]:
locate_baseline(flags)
case ["check-regressions", *args]:

View file

@ -77,6 +77,7 @@ run() {
-e CI \
-e CARGO_TARGET_DIR=/builtins-target \
-e CARGO_TERM_COLOR \
-e MAY_SKIP_LIBM_CI \
-e RUSTFLAGS \
-e RUST_BACKTRACE \
-e RUST_COMPILER_RT_ROOT \

View file

@ -174,6 +174,14 @@ done
# Test libm
# Make sure a simple build works
cargo check -p libm --no-default-features --target "$target"
if [ "${MAY_SKIP_LIBM_CI:-}" = "true" ]; then
echo "skipping libm PR CI"
exit
fi
mflags=()
# We enumerate features manually.
@ -226,9 +234,6 @@ case "$target" in
*windows-gnu) mflags+=(--exclude libm-macros) ;;
esac
# Make sure a simple build works
cargo check -p libm --no-default-features --target "$target"
if [ "${BUILD_ONLY:-}" = "1" ]; then
# If we are on targets that can't run tests, verify that we can build.
cmd=(cargo build --target "$target" --package libm)