Skip to content

Split analysis makefile and GitHub Actions workflow - #294

Merged
jserv merged 5 commits into
sysprog21:mainfrom
henrybear327:build/split-analysis-and-ci
Aug 14, 2026
Merged

Split analysis makefile and GitHub Actions workflow#294
jserv merged 5 commits into
sysprog21:mainfrom
henrybear327:build/split-analysis-and-ci

Conversation

@henrybear327

@henrybear327 henrybear327 commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

It's been a recent trend that that we continue to have merge conflicts regarding the analysis makefile and Github Action main.yml files, and those 2 files have grown to a size that is not easily maintainable.

This PR splits the files by feature area. It's a pure refactoring PR, not intended to do any feature changes.


Summary by cubic

Split the analysis Makefile and CI workflow by concern to reduce conflicts and keep required checks stable. Manual Build dispatches now run HVF runtime tests; check-format fails if shellcheck is missing, and lint depends on the generated dispatch.h.

Review notes

  • Makefile now includes mk/lint.mk, mk/verify.mk, and mk/format.mk; proof targets stay in mk/verify.mk. Targets and recipes are unchanged.
  • Replaced .github/workflows/main.yml with build.yml, lint.yml, static-analysis.yml, and verify.yml; job ids/names, matrices, caches, artifacts, and required status checks are preserved. Concurrency is per file. Build on workflow_dispatch runs runtime tests.
  • Consolidated script readers into scripts/verify-mk.py; check-proof-targets.py reads the verify matrix from verify.yml. Stubs and scripts now reference mk/verify.mk.
  • make lint adds $(DISPATCH_HEADER) as a prerequisite. check-format requires shellcheck and fails fast if missing.

Migration

  • Install shellcheck to run make check-format. No changes needed for developers otherwise or for branch protection settings.

Written for commit 15996f3. Summary will update on new commits.

Review in cubic

mk/analysis.mk held three unrelated things in 595 lines: the
clang-format and shellcheck file lists, the clang-tidy, scan-build and
Infer entry points, and the Frama-C proof targets with the WP machinery
that turns them into rules. Branches touching different halves of it
still conflict.

mk/verify.mk keeps the proofs, mk/lint.mk and mk/format.mk take the
rest. The proof target table stays with the machinery rather than
becoming a fourth file. VERIFY_TARGETS reads $(.VARIABLES) with :=, so
it sees only what make has parsed so far, and check-proof-targets.py
catches a VERIFY_<T>_SRC block written below that line by comparing the
table against "make print-verify-targets". A table in its own file is
always parsed first, which makes that comparison unfalsifiable: the
check would report success for the one mistake it was written to catch.

scripts/analysis-mk.py becomes scripts/verify-mk.py and gains
joined_text(), so check-mutants.py and check-char-signedness.py drop the
private copies of that reader they each carried and the makefile is
named in one place. check-mutants.py's HARNESS_FILES lists mk/verify.mk
in place of mk/analysis.mk; mk/format.mk and mk/lint.mk are left out
because neither can change a proof verdict, which is what that set
selects for.

Targets, recipes and variables are unchanged: "make -n verify" prints
the same recipe for all seventeen proofs, and "make help" lists the same
entries, with print-verify-targets now printing after analyze because
the awk scans MAKEFILE_LIST in include order.
@henrybear327
henrybear327 requested a review from jserv August 14, 2026 10:13
@henrybear327 henrybear327 self-assigned this Aug 14, 2026
cubic-dev-ai[bot]

This comment was marked as resolved.

@jserv jserv changed the title Split analysis makefile and Github CI workflow Split analysis makefile and GitHub Actions workflow Aug 14, 2026
@jserv

jserv commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Use "GitHub Actions" instead of "Github CI" as the latter is confusing.

.github/workflows/main.yml ran ten jobs in 869 lines, so a branch
touching the runtime matrix conflicted with one touching the proof
sharding. It splits along its needs edges, leaving no dependency to
cross a file boundary: lint.yml, build.yml (build-macos ->
runtime-macos), static-analysis.yml (tidy-macos, scan-macos,
infer-macos) and verify.yml (proof-targets -> verify-mutants -> verify
and verify-mutants-gate).

Job ids, job names, steps, env, matrices, caches and artifact names are
copied unchanged, so every required status check keeps the name branch
protection requires. That is also why the jobs are not routed through a
reusable workflow: one called with workflow_call reports as "<caller> /
<callee>", which renames the check and unrequires it silently.

check-proof-targets.py reads the verify-mutants matrix out of
verify.yml, and check-mutants.py's HARNESS_FILES names verify.yml alone,
since that is the file deciding which target a shard passes to --target
and the other three cannot reach the mutation run.

Each file carries its own copy of the on:, concurrency: and permissions:
blocks, so ${{ github.workflow }} now yields four cancellation groups
instead of one. A new push still cancels every in-flight job of a pull
request; cancelling one workflow by hand no longer cancels the rest, and
a manual run takes one workflow_dispatch per file.
runtime-macos was gated to push and pull_request, so "Run workflow" on
Build ran build-macos alone and reported green with every HVF and
sanitizer leg skipped. A skipped required check does not block a merge,
as verify-mutants-gate's comment in verify.yml already records, so a
manual run could stand in for testing that never happened.

The gate predates the split: main.yml carried the same if: under an on:
block that already listed workflow_dispatch. Adding the event rather
than dropping the trigger keeps the manual re-run, which is what the
self-hosted runners need after an outage. The repository guard is
untouched, so a fork still cannot reach them. Only the PR-head guard
step reads pull_request context and it carries its own if:.
On a tree without the generated headers, "make lint" fails:

  src/debug/syscall-hist.c:18:10: error: 'dispatch.h' file not found
  src/syscall/syscall.c:78:10: error: 'dispatch.h' file not found
  Found compiler error(s).
  make: *** [lint] Error 1

The target listed shim_blob.h and version.h only, while clang-tidy runs
over $(SRCS), two of whose files include the dispatch.h that
$(DISPATCH_HEADER) generates. mk/analysis.mk carried the same list, so
the gap predates the split.

tidy-macos hid it with a step that generated all three headers before
calling make lint, which meant CI never exercised the target's own
dependencies. That step goes with the fix.
Without shellcheck on PATH the loop read the shell's 127 as a warning
like any other, so the target ended:

  /bin/sh: shellcheck: command not found
  ...
  39 script(s) have warnings

counting scripts none of which was checked. require-tool, which lint,
analyze and infer-uninit already call, names the tool and the install
and stops there.

The command -v guards on shfmt and black in indent are deliberately the
other shape: a formatter that skips itself changes nothing, while a
checker that skips itself reports a pass it never earned. mk/analysis.mk
carried the same loop, so this predates the split.
@henrybear327
henrybear327 force-pushed the build/split-analysis-and-ci branch from 8966357 to 15996f3 Compare August 14, 2026 13:35
@jserv
jserv merged commit 9b33244 into sysprog21:main Aug 14, 2026
30 checks passed
@henrybear327
henrybear327 deleted the build/split-analysis-and-ci branch August 14, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants