- archive the NATIVE stage-2 binary (/tmp/fpn_blaise2), not the QBE one - COMPILER_ID drops -SNAPSHOT at release, re-gains it at the dev-cycle bump; keep base version synced with project.xml or bif-coverage fails - add fixpoint-warmcache.sh (WARMCACHE_FIXPOINT_OK) to the verification set - rename the -pre bootstrap dir to the next cycle (not refresh-in-place) - define the changelog/community-post range as <prev-tag>..v<X.Y.Z> - note the CI STAGE1_TAG / rolling-bootstrap anchor bump
18 KiB
| name | description | allowed-tools | |||||
|---|---|---|---|---|---|---|---|
| cut-blaise-release | Cut a new Blaise compiler release. Bumps the version in Blaise.pas + project.xml, rebuilds compiler + RTL + stage-2 binary, verifies all four self-hosting fixpoints (QBE, native, internal-assembler, warm-cache), runs the full test suite, commits, tags, archives the NATIVE stage-2 binary under releases/vX.Y.Z/ (NOT committed — releases/ is gitignored), renames + refreshes the -pre bootstrap dir to the next cycle, writes the community post + changelog, then bumps Blaise.pas + project.xml + uCompilerId.pas to the next -SNAPSHOT version. Use when asked to "cut a release", "release v0.X.Y", "tag a release", or after the user confirms a fixpoint is achieved. |
|
Cutting a Blaise release
This skill mirrors the verified release process used for v0.11.0. Run every step in order. Stop and surface the failure to the user if any step fails — never paper over a missing fixpoint.
Inputs
The user will name the release version (e.g. "0.11.0"). If they didn't,
ask them via AskUserQuestion. The next-dev version is the next minor with
a -SNAPSHOT suffix (0.11.0 → 0.12.0-SNAPSHOT). Confirm with the user if unsure.
Preconditions to check before starting
- Run
git status. The working tree often carries unrelated uncommitted changes (e.g.compiler/project.xmlandtools/kanban/project.xmlbuild- option tweaks, plus many untracked.txt/.adocworking files). These are NORMAL on this repo and must NOT be swept into the release commit. The rootproject.xmlmay also carry unrelated edits — you will stage only its version line (see Step 5). If you see uncommitted changes that look like in-progress feature work, ask the user whether to proceed. - The current
Versionconstant incompiler/src/main/pascal/Blaise.passhould look like'<X.Y.Z>-SNAPSHOT'(the dev marker). If it's already a plain non-dev version, ask the user — they may have already cut this release. - All commands run from the project root
/data/devel/new-pascal-compiler(PasBuild requirement — sub-modules lack the<version>field). vendor/qbe/qbemust exist (the fixpoint scripts need it). The current release binary lives at the newestreleases/v*-pre/blaiseorreleases/v*/blaise— the fixpoint scripts auto-pick it viasort -V.
Step 1 — bump version to release
Edit compiler/src/main/pascal/Blaise.pas. Find the line:
Version = '<X.Y.Z>-SNAPSHOT';
and change it to:
Version = '<X.Y.Z>';
Also edit project.xml in the project root. Find the line:
<version><X.Y.Z>-SNAPSHOT</version>
and change it to:
<version><X.Y.Z></version>
Edit compiler/src/main/pascal/uCompilerId.pas. The COMPILER_ID looks like
'blaise-<X.Y.Z>-SNAPSHOT+bif<N>'. For the release, drop the -SNAPSHOT
but keep the +bif<N> tag → 'blaise-<X.Y.Z>+bif<N>'. Only bump the
+bif<N> number if the .bif interface format actually changed this cycle
(IFACE_VERSION in uUnitInterfaceIO.pas — the two must agree).
IMPORTANT (bif-coverage sync): the bif-coverage tool's TestRun_BifCoverage_NoGaps
asserts the project.xml base version (with -SNAPSHOT stripped) is a substring
of COMPILER_ID. So COMPILER_ID and project.xml <version> must carry the
same base version at all times — here at release, and again at the dev-cycle
bump in Step 7. A mismatch silently fails that test.
Step 2 — verify the QBE fixpoint (NOT the binary we ship)
The project's scripts/fixpoint.sh does a whole QBE stage-2 build: it
rebuilds + installs the runtime, uses the latest release binary as stage-1 to
emit stage-2 IR, assembles + links the stage-2 binary to /tmp/fp_blaise2,
then emits stage-3 IR and diffs it.
NOTE: since v0.12.0 the native backend is the default, so the binary we
actually archive and ship is the NATIVE stage-2 binary from Step 3
(/tmp/fpn_blaise2), NOT /tmp/fp_blaise2. This step still runs — the QBE
fixpoint is a required reproducibility guard and prints the IR line count we
quote in the commit message and docs — but /tmp/fp_blaise2 is only used to
rebuild compiler/target/blaise below so the native fixpoints run on the
released version.
./scripts/fixpoint.sh # must print FIXPOINT_OK
Note the stage-2 IR line count it prints ("stage-2 IR: N lines") — you'll quote it in the commit message and the docs.
The verified stage-2 binary is /tmp/fp_blaise2. (If the release stage-1
was too old to reach fixpoint in one round, fixpoint.sh extends a round and
the binary becomes /tmp/fp_blaise3 — read the script's output to see which.)
fixpoint.sh only updated the IR-emitting path; compiler/target/blaise may
still be the old -SNAPSHOT build. Rebuild it from the fixpoint binary so the
native fixpoints and the test runner use the released version:
cp compiler/target/blaise_rtl.a /tmp/blaise_rtl.a # FindRTL looks beside --compiler
pasbuild compile -m blaise-compiler --compiler /tmp/fp_blaise2
compiler/target/blaise --help | head -1 # must print: Blaise Compiler v<X.Y.Z>
ALWAYS pass --compiler to pasbuild — without it, PasBuild falls back to FPC,
which this project does not use.
Step 3 — verify the native, internal-assembler, and warm-cache fixpoints
fixpoint.sh (Step 2) only exercises the QBE backend. Three more fixpoints
guard the native backend, the in-process internal assembler, and warm-cache
(incremental) rebuilds. Run all three:
./scripts/fixpoint-native.sh # must print NATIVE_FIXPOINT_OK
./scripts/fixpoint-native-internal.sh # must print NATIVE_INTERNAL_OK
./scripts/fixpoint-warmcache.sh # must print WARMCACHE_FIXPOINT_OK
fixpoint-native.sh writes the native stage-2 binary to /tmp/fpn_blaise2
— that is the binary we archive and ship (Step 6), because native is the
default backend. Confirm it prints the right version:
/tmp/fpn_blaise2 --help | head -1 # must print: Blaise Compiler v<X.Y.Z>
If any of the four fixpoints does not print its OK line, stop. Show the user the output and do not proceed. There is no acceptable "almost fixpoint" — any divergence means the release is not reproducible.
Step 4 — run the full test suite with the fixpoint binary
Build the TestRunner with the verified stage-2 binary, then run the whole suite
(do NOT use pasbuild test — build the runner and invoke it directly so you see
the real total):
cp compiler/target/blaise_rtl.a /tmp/blaise_rtl.a
pasbuild test-compile -m blaise-compiler --compiler /tmp/fp_blaise2
compiler/target/TestRunner # must print: OK (N tests, ...)
Note the test count N. If any test fails, stop — do not release.
Step 5 — commit and tag
Stage Blaise.pas whole (its only change is the version), but stage only the
version-line hunk of project.xml — it frequently carries unrelated working
changes that must not enter the release commit:
git add compiler/src/main/pascal/Blaise.pas
printf '%s\n' 'y' 'n' 'n' | git add -p project.xml # stage hunk 1 (version) only
git diff --cached --stat # MUST show exactly: Blaise.pas | 2 +- AND project.xml | 2 +-
If git diff --cached --stat shows project.xml with more than 2 changed lines,
you swept in unrelated changes — git restore --staged project.xml and redo the
git add -p. (The hunk-selection answers y n n assume the version line is the
first hunk; if project.xml has a different shape, inspect with git add -p and
stage only the <version> hunk.)
Commit with this pattern (HEREDOC for clean formatting):
release: v<X.Y.Z>
<one or two sentences: fixpoint verified on N lines of QBE IR (all four
fixpoints green: QBE, native, internal-asm, warm-cache), the headline themes
of the cycle, and the test count.>
Then tag:
git tag v<X.Y.Z>
Never reference Claude in the commit message. Do not push. The user pushes manually.
Step 6 — archive the release binary + refresh the -pre bootstrap binary
Archive the native stage-2 binary (/tmp/fpn_blaise2 from Step 3) — native
is the default backend, so the shipped/bootstrap binary must be native, NOT the
QBE /tmp/fp_blaise2:
mkdir -p releases/v<X.Y.Z>
cp /tmp/fpn_blaise2 releases/v<X.Y.Z>/blaise # NATIVE binary from fixpoint-native.sh
chmod +x releases/v<X.Y.Z>/blaise
cp compiler/target/blaise_rtl.a releases/v<X.Y.Z>/blaise_rtl.a
releases/v<X.Y.Z>/blaise --help | head -1 # sanity: Blaise Compiler v<X.Y.Z>
Every release directory must contain both blaise and blaise_rtl.a — the
bootstrap (FindRTL) looks for the archive beside the binary, so a release
missing blaise_rtl.a cannot serve as a stage-1 bootstrap.
Now rename the rolling -pre directory to the next cycle and refresh its
binary to this verified build, so cold-bootstrap of master stays current for
the dev cycle about to open. releases/ is gitignored, so this is a plain
filesystem mv (no git mv):
# The -pre dir from the previous cycle is named v<X.Y.Z>-pre. Rename it to the
# NEXT minor so the newest releases/v*-pre/ tracks the cycle we are opening.
mv releases/v<X.Y.Z>-pre releases/v<next-minor>.0-pre # e.g. v0.12.0-pre -> v0.13.0-pre
cp /tmp/fpn_blaise2 releases/v<next-minor>.0-pre/blaise
cp compiler/target/blaise_rtl.a releases/v<next-minor>.0-pre/blaise_rtl.a
rm -f releases/v<next-minor>.0-pre/*.bak # drop any stale backup cruft
releases/v<next-minor>.0-pre/blaise --help | head -1 # sanity check
(If no -pre directory exists yet, create releases/v<next-minor>.0-pre/
directly. The GitHub Actions CI and rolling-bootstrap.sh both look for the
newest releases/v*-pre/, so the name must be the cycle just opened — not the
one just released.)
Update the test count in README.adoc (the "Testing: N tests" line in the
Project Status section) to match the suite total from Step 4, then commit it:
git add README.adoc
git commit -m "docs: update test count to <N> for v<X.Y.Z>"
Then build the release tarball for the GitHub Releases page — a single
top-level directory containing blaise, blaise_rtl.a, README.adoc,
NOTICE, and LICENSE:
STAGING=$(mktemp -d)
DIRNAME="blaise-v<X.Y.Z>-linux-x86_64"
mkdir "$STAGING/$DIRNAME"
cp releases/v<X.Y.Z>/blaise "$STAGING/$DIRNAME/blaise"
cp releases/v<X.Y.Z>/blaise_rtl.a "$STAGING/$DIRNAME/blaise_rtl.a"
cp README.adoc "$STAGING/$DIRNAME/README.adoc"
cp NOTICE "$STAGING/$DIRNAME/NOTICE"
cp LICENSE "$STAGING/$DIRNAME/LICENSE"
tar -czf releases/blaise-v<X.Y.Z>-linux-x86_64.tar.gz -C "$STAGING" "$DIRNAME"
rm -rf "$STAGING"
tar -tzf releases/blaise-v<X.Y.Z>-linux-x86_64.tar.gz # verify
Do not git add any binary files or the tarball. releases/ is gitignored
and the user keeps binaries local on purpose.
Step 7 — bump to next -dev version
Edit compiler/src/main/pascal/Blaise.pas again, changing '<X.Y.Z>' to
'<next-minor>.0-SNAPSHOT' (e.g. 0.11.0 → 0.12.0-SNAPSHOT). Use
-SNAPSHOT in BOTH files — the older -dev suffix in Blaise.pas was retired so
the two version strings stay in sync.
Also edit project.xml, changing <version> to <next-minor>.0-SNAPSHOT.
REQUIRED: also bump compiler/src/main/pascal/uCompilerId.pas back to the
-SNAPSHOT form for the new base version, keeping the same +bif<N> tag →
'blaise-<next-minor>.0-SNAPSHOT+bif<N>'. This keeps COMPILER_ID in sync
with project.xml's base version — otherwise bif-coverage's
TestRun_BifCoverage_NoGaps fails (it checks the project base version is a
substring of COMPILER_ID). Do NOT bump +bif<N> here; the .bif format
hasn't changed just by opening a dev cycle.
Stage selectively again (project.xml may still carry unrelated changes):
git add compiler/src/main/pascal/Blaise.pas compiler/src/main/pascal/uCompilerId.pas
printf '%s\n' 'y' 'n' 'n' | git add -p project.xml
git diff --cached --stat # Blaise.pas, uCompilerId.pas, project.xml — version lines only
git commit -m "chore: begin v<next-minor>.0-dev cycle"
NOTE on CI / rolling-bootstrap anchor: when this release becomes the new
cold-bootstrap baseline, bump STAGE1_TAG in .github/workflows/bootstrap.yml
and the rolling-bootstrap default---from anchor to v<X.Y.Z>. This is a
CI-only change and typically lands in its own commit alongside the release
(it does not block the release itself).
Step 8 — write community post and changelog
Write two documents at the project root. Both are untracked (do not git add
them) — they are working files the user edits and publishes manually.
Both documents cover only the commits between the previous release tag and
the tag being cut now — the range <prev-tag>..v<X.Y.Z>, where <prev-tag>
is the most recent existing release tag before this one. Find it with
git describe --tags --abbrev=0 v<X.Y.Z>^ (the latest tag reachable from the
commit just before the release commit), or git tag --sort=-v:refname | head.
Do NOT summarise the whole history — only this cycle's delta.
Survey the commits first (the v<X.Y.Z> tag already exists from Step 5):
git log <prev-tag>..v<X.Y.Z> --oneline, and group by theme. Helpful groupings:
git log <prev-tag>..v<X.Y.Z> --oneline | grep ' feat' (features),
... | grep ' fix' (fixes), ... | grep ' perf' (performance).
community-post-v<X.Y.Z>.md
A warm, readable announcement for the Blaise community (forums, mailing lists, social media). Style:
- Not a commit-by-commit log — group changes into themes with a clear headline win up top.
- Sprinkle emoji throughout section headings and milestone call-outs.
- Warm, collaborative voice; British English spelling (see the global writing style guide).
- Include tiny code examples for the headline language features — a 3–6
line snippet per feature is far more compelling than prose. VERIFY each
snippet actually compiles before publishing: write it to
/tmp/snip.pasand runcompiler/target/blaise --source /tmp/snip.pas --output /tmp/snip(try both--backend qbeand--backend nativefor codegen-sensitive features). - Cover: headline win(s), language/stdlib additions, bug-fix hardening, notable compiler internals, the fixpoint line count and test count, and a closing call to action pointing at the GitHub release page.
- End with a forward-looking line ("Onwards to v! 🙌").
- Reference
changelog-v<X.Y.Z>.mdfor full details.
changelog-v<X.Y.Z>.md
A technical changelog for the GitHub Releases page. Style:
- Grouped by area (Native backend, Toolchain, Language, ARC/runtime, stdlib, Codegen, Semantic, Debugging, Performance, Tooling/CI).
- Each entry is one concise line: what changed and why it matters. More technical than the community post.
- A short "Examples" block under the Language section showing the new syntax is welcome (same verify-it-compiles rule applies).
- Close with the fixpoint IR line count and the test count.
Step 9 — report to the user
Summarise:
- Tag created (e.g.
v<X.Y.Z>) - Stage-2 IR line count and all four OK lines: "FIXPOINT_OK / NATIVE_FIXPOINT_OK / NATIVE_INTERNAL_OK / WARMCACHE_FIXPOINT_OK"
- Test count (N tests passing)
- Binary path:
releases/v<X.Y.Z>/blaise(note: not in git) -prebootstrap binary refreshed- New dev version on master
- Last 3–4 commit oneliners
- Community post:
community-post-v<X.Y.Z>.md(untracked) - Changelog:
changelog-v<X.Y.Z>.md(untracked) - Release tarball:
releases/blaise-v<X.Y.Z>-linux-x86_64.tar.gz(upload to GitHub Releases)
Mention that the user must git push --tags themselves if they want the tag on
the remote — never push automatically.
Common pitfalls
- Unrelated changes swept into the release commit.
git add project.xmlstages the WHOLE file, including pre-existing build-option edits. Alwaysgit add -pand confirmgit diff --cached --statshows exactly two one-line changes before committing. (This bit the v0.11.0 cut — the commit had to be reset and redone.) - Archiving the QBE binary instead of the native one. Native is the default
backend, so the shipped/bootstrap binary must be
/tmp/fpn_blaise2(fromfixpoint-native.sh), NOT the QBE/tmp/fp_blaise2(fromfixpoint.sh). The QBE fixpoint is still required as a reproducibility guard — it just isn't the artefact. - Stale fixpoint binary in /tmp. A
/tmp/fpn_blaise2(or/tmp/fp_blaise3) left over from an earlier run can be days old. Always confirm the binary you archive prints the RIGHT version:<binary> --help | head -1. - COMPILER_ID out of sync with project.xml → bif-coverage fails. At release
COMPILER_IDdrops-SNAPSHOT; at the dev-cycle bump it must re-gain-SNAPSHOTfor the NEW base version. The base version inCOMPILER_IDmust always match project.xml's<version>base, orTestRun_BifCoverage_NoGapsfails. Keep the+bif<N>tag unchanged unless the.bifformat changed. - Forgetting to rename the -pre dir. The newest
releases/v*-pre/must name the cycle being OPENED, not the one just released. After cutting v<X.Y.Z>,mv releases/v<X.Y.Z>-pre releases/v<next-minor>.0-pre(filesystem move — releases/ is gitignored). CI and rolling-bootstrap pick the newest by name. compiler/target/blaisestill on the old version.fixpoint.shdoes not rebuild it; do thepasbuild compile --compiler /tmp/fp_blaise2step (Step 2) before the native fixpoints, or they run against the previous version.- Forgetting
--compiler. Everypasbuildcommand must pass--compiler <blaise-binary>; without it PasBuild falls back to FPC, which is not part of this toolchain. - Missing
blaise_rtl.abeside--compiler. When pointing--compilerat/tmp/fp_blaise2, copycompiler/target/blaise_rtl.ato/tmp/blaise_rtl.afirst —FindRTLlooks beside the binary, and a stale archive causes undefined-reference link errors. - Wrong working directory. All
pasbuildcommands fail outside the project root with a confusing version error. Stay at/data/devel/new-pascal-compiler. - Backgrounded fixpoint output drops. Run the fixpoint scripts in the foreground with a generous timeout (600000 ms). Self-built compilation of the compiler takes ~2 min per stage.