-
Notifications
You must be signed in to change notification settings - Fork 28
ci: adopt shared tccbin conformance test suite (freebsd-amd64) #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
GGRei
merged 23 commits into
vlang:thirdparty-freebsd-amd64
from
quaesitor-scientiam:add-shared-conformance-ci-freebsd-amd64
Jul 26, 2026
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
7a8a5de
ci: adopt shared tccbin conformance test suite
PythonWillRule 4fd6d3a
ci: add temporary diagnostic step to surface the compile error
PythonWillRule 9cd7cec
ci: add temporary job to verify candidate tinycc etext/end fix
PythonWillRule 37059ac
ci: retry (repo.or.cz connection reset on prior attempt)
PythonWillRule d689605
ci: clone+patch tinycc on the runner, not inside the VM (repo.or.cz c…
PythonWillRule f8d776a
ci: fix patch file path (git -C changes cwd, relative path was wrong)
PythonWillRule eaa3217
ci: use git:// protocol for repo.or.cz (https is currently broken the…
PythonWillRule a83356a
ci: use system cc (clang) instead of gcc, matching update_tccbin.yml'…
PythonWillRule 4d6ada5
ci: run gmake install so the configured --prefix path actually exists
PythonWillRule 146aec2
ci: finalize with XFAIL lane for the tcc etext/end limitation
PythonWillRule b591f24
ci: test the PR's own revision, and require SIGSEGV specifically
PythonWillRule 69d6496
ci: fix environment_variables forwarding (needs job-level env, not st…
PythonWillRule b37132d
ci: fix job-level env referencing steps context (schema violation - G…
PythonWillRule bf1697f
ci: move env to job level (step-level env on a uses: step wasn't forw…
PythonWillRule f7ae4ae
ci: fix Codex-flagged CI hardening issues (pin SHA, pin action, fetch…
PythonWillRule fd124a3
ci: fix mkdir (needs -p, thirdparty/ parent dir doesn't exist yet in …
PythonWillRule 2df2141
ci: harden libgc.a XFAIL to assert the actual etext/end error, not ju…
PythonWillRule c2748f0
ci: fix set -e aborting before the XFAIL case statement (direct_err c…
PythonWillRule 340d9f4
ci: validate both gc_alloc.c and hello.c's direct errors in the libgc…
PythonWillRule 5cf3efe
ci: reject additional linker errors alongside the known etext/end sig…
PythonWillRule ea3b81b
ci: fix 2 more Codex findings (broader exclusivity check, positive no…
PythonWillRule 5d8211f
ci: preserve XFAIL probe exit codes, not just the captured text
PythonWillRule d9212d5
ci: test the actual PR merge commit, not just the head branch tip in …
PythonWillRule File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,291 @@ | ||
| name: build and test (freebsd-amd64) | ||
|
|
||
| on: | ||
| push: | ||
| branches: [thirdparty-freebsd-amd64] | ||
| pull_request: | ||
| branches: [thirdparty-freebsd-amd64] | ||
| workflow_dispatch: {} | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| # `github.sha` and `github.event.repository.clone_url` are used | ||
| # uniformly across all 3 trigger event types, and correctly cover | ||
| # each: | ||
| # - pull_request: `github.sha` is GitHub's own synthesized MERGE | ||
| # commit (what the target branch would actually look like if | ||
| # this PR were merged right now), not the PR branch's own head - | ||
| # testing only the head tree in isolation could pass a PR whose | ||
| # OWN commit works fine, while the actual merge result (combined | ||
| # with whatever the target branch has since gained) is broken | ||
| # (Codex pullrequestreview-4781865117 on vlang/tccbin#75). That | ||
| # merge commit exists ONLY as an object in the base repo | ||
| # (vlang/tccbin, exposed via `refs/pull/<n>/merge`) - never in a | ||
| # contributor's fork - so this must clone from | ||
| # `github.event.repository.clone_url` (the base repo), not the | ||
| # fork's URL, regardless of where the PR originated. | ||
| # - push/workflow_dispatch: `github.sha`/`github.event.repository` | ||
| # are simply the pushed/dispatched commit and the repo it lives | ||
| # in - already exactly what should be tested. | ||
| # In every case, `github.sha` is fixed for this run's entire | ||
| # lifetime once the triggering event fires (a later push creates a | ||
| # brand new run with its own new `github.sha`, it doesn't mutate | ||
| # this one) - so this is exactly as race-safe against a | ||
| # mid-queue push as the earlier head.sha-based approach was (Codex | ||
| # pullrequestreview-4780048339 on vlang/tccbin#75), while also | ||
| # testing the actual merge result instead of the head tree alone. | ||
| # Set at job level (matching update_tccbin.yml's own working BSD | ||
| # job env, which only ever references github.*/secrets.*, never | ||
| # steps.* - step-level env on the "Start VM" step was tried first | ||
| # and empirically did NOT get forwarded into the VM by | ||
| # environment_variables, unlike this job-level placement). | ||
| env: | ||
| TCCBIN_CLONE_URL: ${{ github.event.repository.clone_url }} | ||
| TCCBIN_CLONE_SHA: ${{ github.sha }} | ||
| steps: | ||
| # cross-platform-actions/action boots a real FreeBSD VM inside this | ||
| # Linux runner - the same action vlang/v's own update_tccbin.yml | ||
| # uses to rebuild this branch's binaries. Cloning both repos | ||
| # directly inside the VM (rather than actions/checkout on the | ||
| # runner + sync_files) mirrors that proven-working pattern instead | ||
| # of relying on an unverified file-permission-preserving sync. | ||
| # Pinned to the v1.3.0 tag's commit (resolved via the GitHub API) | ||
| # rather than the mutable tag itself, so a retag upstream can't | ||
| # silently swap in different third-party code here without a | ||
| # review-visible diff (Codex pullrequestreview-4780048339 on | ||
| # vlang/tccbin#75). | ||
| - name: Start freebsd-amd64 VM | ||
| uses: cross-platform-actions/action@4347c661239bf5dcd0cd77708061488d907343d6 # v1.3.0 | ||
| with: | ||
| operating_system: freebsd | ||
| version: '15.1' | ||
| memory: 4G | ||
| shell: sh | ||
| sync_files: runner-to-vm | ||
| environment_variables: TCCBIN_CLONE_URL TCCBIN_CLONE_SHA | ||
|
|
||
| - name: fetch shared conformance suite and this branch's binaries | ||
| shell: cpa.sh {0} | ||
| run: | | ||
| set -eu | ||
| sudo pkg install -y bash git | ||
|
|
||
| # Fetch the exact commit that triggered this run, not a branch | ||
| # name - `git clone --branch <name>` would resolve whatever | ||
| # that name points to *at clone time*, which can race a push | ||
| # that lands after this run started (Codex pullrequestreview- | ||
| # 4780048339 on vlang/tccbin#75). | ||
| mkdir -p thirdparty/tcc && cd thirdparty/tcc | ||
| git init -q | ||
| git remote add origin "$TCCBIN_CLONE_URL" | ||
| git fetch --depth=1 origin "$TCCBIN_CLONE_SHA" | ||
| git checkout -q FETCH_HEAD | ||
| cd ../.. | ||
|
|
||
| # thirdparty/libgc/include (gc.h) and thirdparty/tccbin_tests | ||
| # (the shared cross-platform conformance suite) both live in | ||
| # the main v repo, not here. A plain `git clone --depth=1` | ||
| # only ever contains the CURRENT tip commit's objects - once | ||
| # vlang/v advances past this pinned SHA, that historical commit | ||
| # object won't exist in a fresh shallow clone and this checkout | ||
| # would fail (Codex P1, pullrequestreview-4780048339 on | ||
| # vlang/tccbin#75, line 60). Fetch that exact SHA directly | ||
| # instead of cloning HEAD and hoping it's still there. | ||
| mkdir -p vsrc && cd vsrc | ||
| git init -q | ||
| git remote add origin https://github.com/vlang/v.git | ||
| git sparse-checkout init --no-cone | ||
| git sparse-checkout set thirdparty/libgc thirdparty/tccbin_tests | ||
| git fetch --filter=blob:none --depth=1 origin c82d3f08271e9324d6fb8de3c251e9c0e1a9154b | ||
| git checkout -q FETCH_HEAD | ||
| cd .. | ||
|
|
||
| chmod +x thirdparty/tcc/tcc.exe | ||
|
|
||
| # tcc on FreeBSD cannot currently link the bundled libgc.a: its | ||
| # linker only ever defines the glibc-style _etext/_edata/_end | ||
| # (see tccelf.c's tcc_add_linker_symbols), never the plain BSD- | ||
| # style etext/edata/end that BDWGC's FreeBSD data-segment-scanning | ||
| # code references directly - so any GC-dependent program fails | ||
| # with "undefined symbol 'etext'"/"'end'". Root-caused and a fix | ||
| # verified against a real FreeBSD VM (patched tcc successfully | ||
| # builds+runs shared/hello.c against this same libgc.a); the fix | ||
| # is being submitted upstream to tinycc-devel but isn't in any | ||
| # shipped tcc.exe yet, so it can't be relied on here. | ||
| # | ||
| # crash.c doesn't touch the GC at all, so it's checked directly | ||
| # (bypassing run.sh, which would otherwise apply the same GC | ||
| # flags to every test) as the real, blocking regression check for | ||
| # this platform's tcc/libtcc1.a/crt pairing. | ||
| - name: run shared conformance tests (crash only, no GC - blocking) | ||
| shell: cpa.sh {0} | ||
| run: | | ||
| set -eu | ||
| thirdparty/tcc/tcc.exe vsrc/thirdparty/tccbin_tests/shared/crash.c \ | ||
| -o /tmp/crash_nogc | ||
|
|
||
| set +e | ||
| /tmp/crash_nogc | ||
| code=$? | ||
| set -e | ||
|
|
||
| # Any nonzero exit isn't enough: an exec-format error, a | ||
| # missing dynamic loader, or an unrelated miscompilation that | ||
| # just exit(1)s would also satisfy that and silently mask a | ||
| # real regression, since the GC-backed suite below is | ||
| # non-blocking. crash.c's null-pointer dereference is | ||
| # expected to be killed by SIGSEGV specifically, which a | ||
| # POSIX shell reports as exit code 128+11=139 - verify that | ||
| # exact signal-terminated signature, not just "nonzero". | ||
| if [ "$code" -ne 139 ]; then | ||
| echo "expected exit 139 (killed by SIGSEGV) from an unguarded null-pointer dereference, got $code" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "no-GC compile+crash check passed (killed by SIGSEGV as expected)" | ||
|
|
||
| # crash.c exiting 139 only proves SOME crash happened - if a | ||
| # broken libtcc1.a/crt or startup path made EVERY program | ||
| # segfault immediately (before ever reaching the intentional | ||
| # null-pointer dereference), this check would still see exit | ||
| # 139 and wrongly call it "expected" (Codex pullrequestreview- | ||
| # 4781468264 on vlang/tccbin#75). The GC-backed lane below | ||
| # can't provide this positive check either, since it's | ||
| # expected to fail at link time. Compile and run a trivial, | ||
| # non-crashing program with the same no-GC flags, requiring it | ||
| # to actually complete and exit 0 - proving the toolchain can | ||
| # still produce a genuinely working binary, not just one that | ||
| # crashes regardless of source. | ||
| cat > /tmp/nogc_trivial.c <<'TRIVIALEOF' | ||
| int main(void) { return 0; } | ||
| TRIVIALEOF | ||
| thirdparty/tcc/tcc.exe /tmp/nogc_trivial.c -o /tmp/nogc_trivial | ||
| set +e | ||
| /tmp/nogc_trivial | ||
| trivial_code=$? | ||
| set -e | ||
| if [ "$trivial_code" -ne 0 ]; then | ||
| echo "expected the trivial no-GC program to exit 0, got $trivial_code - the no-GC toolchain path may be broken (e.g. crashing at startup) even though the crash.c check above happened to pass" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "trivial no-GC success check passed (exit=0)" | ||
|
|
||
| # continue-on-error alone would also swallow an unrelated failure | ||
| # (a corrupted libgc.a, a bad checkout, run.sh going missing) as if | ||
| # it were the known etext/end failure, and the blocking crash-only | ||
| # step above never references libgc.a at all - so a real | ||
| # regression in the archive itself could pass CI unnoticed (Codex | ||
| # pullrequestreview-4780048339 on vlang/tccbin#75, line 107). | ||
| # Assert the exact known failure signature instead, mirroring | ||
| # macos-arm64's own libgc.a XFAIL lane: any other failure shape | ||
| # (or an unexpected full pass, meaning the etext/end fix landed) | ||
| # fails the job for real. | ||
| - name: run shared conformance tests (libgc.a - XFAIL until the etext/end tcc fix ships) | ||
| shell: cpa.sh {0} | ||
| run: | | ||
| set +e | ||
| output=$(bash vsrc/thirdparty/tccbin_tests/run.sh "$PWD/thirdparty/tcc/tcc.exe" freebsd -- \ | ||
| -DGC_BUILTIN_ATOMIC=1 -DBUS_PAGE_FAULT=T_PAGEFLT -DALL_INTERIOR_POINTERS=1 \ | ||
| -I "$PWD/vsrc/thirdparty/libgc/include" \ | ||
| "$PWD/thirdparty/tcc/lib/libgc.a" \ | ||
| -lpthread 2>&1) | ||
| code=$? | ||
| set -e | ||
| echo "$output" | ||
|
|
||
| if [ "$code" -eq 0 ]; then | ||
| echo "expected the known etext/end unresolved-symbol failure, but this lane fully passed - the upstream tcc fix must have landed; remove this XFAIL special-casing and fold it back into a single blocking lane." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The pinned run.sh only ever prints "(compile error)" with no | ||
| # detail, so the summary-line match above can't tell "the known | ||
| # etext/end bug" apart from a DIFFERENT static-link regression | ||
| # that happens to fail the same two tests - e.g. a PR that | ||
| # replaces libgc.a with a valid-but-incomplete archive lacking | ||
| # the GC objects entirely: crash.c still compiles (it never | ||
| # references GC symbols) while gc_alloc/hello both report | ||
| # "compile error", matching this same summary shape for an | ||
| # unrelated reason (Codex pullrequestreview-4780219278 on | ||
| # vlang/tccbin#75). Compile BOTH failing tests directly to | ||
| # capture their real tcc stderr and assert each is specifically | ||
| # the known etext/end unresolved-symbol error - checking only | ||
| # one of the two (e.g. hello.c) would let an unrelated | ||
| # regression isolated to the OTHER test (gc_alloc.c) hide | ||
| # behind this same summary shape and pass unnoticed (Codex | ||
| # pullrequestreview-4780815399 on vlang/tccbin#75). | ||
| set +e | ||
| direct_err_hello=$(thirdparty/tcc/tcc.exe vsrc/thirdparty/tccbin_tests/shared/hello.c \ | ||
| -DGC_BUILTIN_ATOMIC=1 -DBUS_PAGE_FAULT=T_PAGEFLT -DALL_INTERIOR_POINTERS=1 \ | ||
| -I vsrc/thirdparty/libgc/include \ | ||
| thirdparty/tcc/lib/libgc.a \ | ||
| -lpthread \ | ||
| -o /tmp/hello_xfail_probe 2>&1) | ||
| direct_code_hello=$? | ||
| direct_err_gc_alloc=$(thirdparty/tcc/tcc.exe vsrc/thirdparty/tccbin_tests/shared/gc_alloc.c \ | ||
| -DGC_BUILTIN_ATOMIC=1 -DBUS_PAGE_FAULT=T_PAGEFLT -DALL_INTERIOR_POINTERS=1 \ | ||
| -I vsrc/thirdparty/libgc/include \ | ||
| thirdparty/tcc/lib/libgc.a \ | ||
| -lpthread \ | ||
| -o /tmp/gc_alloc_xfail_probe 2>&1) | ||
| direct_code_gc_alloc=$? | ||
| set -e | ||
|
|
||
| # Unlike macos-amd64's whole-archive-unparseable bug, this | ||
| # FreeBSD bug is narrow: tcc's linker only ever fails to define | ||
| # the two plain BSD-style symbols (etext, end) that BDWGC | ||
| # references directly - nothing else in the archive is | ||
| # affected. So an exact match here is the correct check: reject | ||
| # if any unresolved-reference line names a symbol OTHER than | ||
| # etext/end, since that would mean a genuinely different, | ||
| # additional regression rode along with the known bug (Codex | ||
| # pullrequestreview-4780907186 on vlang/tccbin#75). | ||
| is_known_etext_end() { | ||
| # $1 = captured stderr text, $2 = the probe's own exit code. | ||
| # A regression could make tcc print the expected etext/end | ||
| # diagnostic and then crash/abort rather than exiting | ||
| # cleanly with its normal compile-error status - the earlier | ||
| # `|| true` discarded that exit code entirely, so a shell | ||
| # "Segmentation fault" or similar abnormal termination | ||
| # alongside the expected text would still pass (Codex | ||
| # pullrequestreview-4781865117 on vlang/tccbin#75). A POSIX | ||
| # shell reports a signal-terminated process as exit code | ||
| # 128+signal; reject those instead of treating any nonzero | ||
| # exit as "the expected compile-error status". | ||
| if [ "$2" -gt 128 ]; then | ||
| return 1 | ||
| fi | ||
| case "$1" in | ||
| *"undefined symbol 'etext'"*|*"undefined symbol 'end'"*) ;; | ||
| *) return 1 ;; | ||
|
quaesitor-scientiam marked this conversation as resolved.
|
||
| esac | ||
| # Filtering only "undefined symbol '...'" lines and checking | ||
| # their NAMES misses a wholly DIFFERENT kind of tcc error | ||
| # (invalid object, relocation error, etc.) riding alongside | ||
| # the known one - such a line simply wouldn't match the | ||
| # "undefined symbol" pattern at extraction, so `other` would | ||
| # stay empty and this would wrongly accept the XFAIL (Codex | ||
| # pullrequestreview-4781468264 on vlang/tccbin#75). Match on | ||
| # tcc's actual error-line prefix instead, so ANY tcc error | ||
| # line that isn't specifically etext/end gets caught, not | ||
| # just a differently-named undefined symbol. | ||
| other=$(printf '%s\n' "$1" | grep -E '^tcc: error:' | grep -v -E "undefined symbol '(etext|end)'") | ||
| [ -z "$other" ] | ||
| } | ||
|
|
||
| case "$output" in | ||
| *"PASS crash"*"FAIL gc_alloc (compile error)"*"FAIL hello (compile error)"*) | ||
| if is_known_etext_end "$direct_err_hello" "$direct_code_hello" && is_known_etext_end "$direct_err_gc_alloc" "$direct_code_gc_alloc"; then | ||
| echo "known XFAIL: etext/end still unresolved on FreeBSD tcc for both gc_alloc.c and hello.c, as expected - not blocking CI" | ||
| else | ||
| echo "libgc.a lane failed with the known summary shape, but at least one of gc_alloc.c/hello.c's direct compile errors does NOT mention an unresolved etext/end symbol (or exited abnormally) - this looks like a different, unexpected regression:" >&2 | ||
| echo "hello.c: exit=$direct_code_hello: $direct_err_hello" >&2 | ||
| echo "gc_alloc.c: exit=$direct_code_gc_alloc: $direct_err_gc_alloc" >&2 | ||
| exit 1 | ||
| fi | ||
| ;; | ||
| *) | ||
| echo "libgc.a lane failed, but NOT with the known/expected summary (PASS crash, FAIL gc_alloc/hello with compile error) - this looks like a different, unexpected problem and should not be silently treated as the known XFAIL." >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.