pkg-xcompile-test
Skill930 starsChanged 6 months agoMITView on GitHub
Cross-compile test harness for @yao-pkg/pkg. Builds a tiny hello.js for every (mode × target) combination and runs what can be executed on a Linux host (native x64, arm64 via docker+qemu, win-x64 via docker-wine). Use when the user wants to verify pkg cross-compilation claims, reproduce issues #87/#181, sanity-check a pkg PR, or compare Standard vs Enhanced SEA across Node 20/22/24. Trigger: "test cross-compile", "run pkg matrix", "verify xcompile", or /pkg-xcompile-test.
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 81
Run from the repo root (paths in the examples are relative to it).
- Very specific: line 176
Not verified — see "Host requirements" above.
- Very specific: line 231
Do not reuse cached binaries between matrix runs — delete bin-node${major}/ first if you rebuilt pkg.
- Very specific: line 234
If you add a new target to the matrix, also add a runner branch in run_one().
- Very specific: line 236
If scottyhardy/docker-wine becomes unavailable, tobix/wine is a smaller drop-in replacement — it has the same EBADF stdout gotcha.
- Very specific: line 238
Don't confuse "builds with warning" with "builds clean".
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
pkg cross-compile test harness
Validates pkg cross-OS and cross-arch support across Node 20/22/24 and
Standard vs Enhanced SEA modes. Produces a consistent result table so
claims in docs and issues can be checked against reality.
When to use
- User is about to edit docs about cross-compile support and needs ground truth.
- User is looking at a cross-compile issue (e.g. #87, #181) and wants to reproduce it.
- User bumped
pkg-fetchor touched bootstrap/prelude and wants a smoke test across targets. - User wants to know whether a regression is Node-version specific.
Host requirements
This harness is designed for a Linux x86_64 host with:
nvmwith node 20, 22, and 24 installed (nvm install 20 22 24).dockerdaemon running.dockerimagescottyhardy/docker-wine(pulled on first win-x64 run).tonistiigi/binfmtinstalled for cross-arch containers — if you seeexec format erroron arm64 runs, install with:docker run --privileged --rm tonistiigi/binfmt --install arm64pkgbuilt from this repo (yarn build) solib-es5/bin.jsexists. The script resolves the pkg entry from its own location — it lives at.claude/skills/pkg-xcompile-test/run-matrix.shand defaultsPKG_BINto<repo-root>/lib-es5/bin.js.
Not supported: macOS runtime verification. docker-osx needs /dev/kvm
and darling is dead. macOS regressions have to be caught on a real Mac
or on the GitHub Actions macos-* runners — note that in the final report.
What it runs
For each Node major (20, 22, 24) the harness:
- Uses
nvm use $majorso the pkg host node matches the target node major. This matters — SEA's blob generator uses hostexecPathwhen host-major == target-major, otherwise tries to run the downloaded target-arch node binary (which fails without cross-arch emulation). - Loops over modes × targets:
- Modes:
std,std-public(--public-packages "*" --public),sea - Targets:
linux-x64,linux-arm64,win-x64,macos-x64,macos-arm64
- Modes:
- Records build result (OK / FAIL), then tries to run the binary:
- linux-x64 → native
- linux-arm64 →
docker run --platform linux/arm64 ubuntu:latest - win-x64 →
docker run scottyhardy/docker-wine(see gotcha below) - macos-* → skipped (
SKIP-no-mac)
- Prints a summary table.
Wine gotcha
wine inside a non-tty docker container produces invalid stdio file
descriptors, causing Node to crash with Error: open EBADF before any
user code runs. The workaround is to redirect wine's stdout/stderr to
files inside the container, then cat them back:
docker run --rm -v "$BINDIR:/mnt" scottyhardy/docker-wine \
bash -c "wine '/mnt/app.exe' </dev/null >/tmp/out 2>/tmp/err; cat /tmp/out"Without this, every wine run will look like a pkg failure when it isn't.
Usage
Run from the repo root (paths in the examples are relative to it).
Quick run (single node version)
# Runs the full matrix for node 22 targets with node 22 as host
./.claude/skills/pkg-xcompile-test/run-matrix.sh 22Full sweep (20 + 22 + 24)
for V in 20 22 24; do
./.claude/skills/pkg-xcompile-test/run-matrix.sh $V
doneCustom pkg build
./.claude/skills/pkg-xcompile-test/run-matrix.sh 22 /path/to/other/pkg/lib-es5/bin.jsCustom work directory
By default build outputs go to /tmp/pkg-xcompile/bin-node<major>/. Override with:
PKG_XCOMPILE_WORKDIR=/somewhere/else ./.claude/skills/pkg-xcompile-test/run-matrix.sh 22Reading the output
Each cell is BUILD / RUN:
OK / OK— worksOK / FAIL— built, but runtime error on targetFAIL / n/a— build failedOK / SKIP-no-mac— can't test macOS runtime on this host
Known results (captured 2026-04-15)
Host: Ubuntu 24.04 x86_64, pkg HEAD of docs/github-pages-site. Tests run
with matching host-node / target-node major.
Node 20
| target | std | std-public | sea |
| linux-x64 | OK / OK | OK / OK | FAIL — SEA needs host ≥ 22 |
| linux-arm64 | OK / OK | OK / OK | FAIL (same) |
| win-x64 | OK / OK | OK / OK | FAIL (same) |
| macos-x64 | OK / n/a | OK / n/a | FAIL (same) |
| macos-arm64 | OK / n/a | OK / n/a | FAIL (same) |
Standard cross-compile on Node 20 works without workarounds.
SEA mode is unavailable because pkg enforces host node ≥ 22.
Node 22
| target | std | std-public | sea |
| linux-x64 | OK / OK | OK / OK | OK / OK |
| linux-arm64 | OK / FAIL | OK / OK | OK / OK |
| win-x64 | OK / FAIL | OK / OK | OK / OK |
| macos-x64 | OK / n/a | OK / n/a | OK / n/a |
| macos-arm64 | OK / n/a | OK / n/a | OK / n/a |
Standard cross-compile on Node 22 is broken:
linux-arm64crashes at runtime withError: UNEXPECTED-20inreadFileFromSnapshot. Matches the #181 failure mode.win-x64exits silently with EXIT=4 and no stdout. Matches the #87 Windows silent-exit bug.
Both are fixed by adding --public-packages "*" --public (which skips the
V8 bytecode step). Enhanced SEA avoids both out of the box.
Node 24
| target | std | std-public | sea |
| linux-x64 | OK / OK | OK / OK | OK / OK |
| linux-arm64 | OK / OK | OK / OK | OK / OK |
| win-x64 | OK / OK | OK / OK | OK / OK |
| macos-x64 | OK / n/a | OK / n/a | OK / n/a |
| macos-arm64 | OK / n/a | OK / n/a | OK / n/a |
Node 24 works out of the box for Standard and SEA, same as Node 20.
The Node 22 bug is a Node-22-specific pkg-fetch patch regression, not
a permanent Standard-mode limitation.
macOS runtime
Not verified — see "Host requirements" above. The regressions tracked
in #181 (macOS host, Node 22+) must be confirmed on a real Mac.
GitHub Actions' macos-13 / macos-14 runners are the right place for
that; a follow-up CI workflow that runs the hello.js matrix there would
close the last hole.
Debugging a failing run
Each cell writes its own log; paths are printed on stderr next to any
FAIL and re-summarised at the end of the run.
- Log location —
$PKG_XCOMPILE_WORKDIR/logs/node<major>/build-<mode>-<target>.log(andrun-<mode>-<target>.log). Default workdir is/tmp/pkg-xcompile. Logs are per-cell so a failing build is not overwritten by the next one. - Re-run one cell — the fastest way is still to invoke
pkgdirectly against the same fixture:cd /tmp/pkg-xcompile node /path/to/pkg/lib-es5/bin.js hello.js -t node22-linux-arm64 \ -o /tmp/pkg-xcompile/bin-node22/std-linux-arm64 exec format erroron arm64 runs — binfmt is not registered. After a reboottonistiigi/binfmtneeds to be re-installed:docker run --privileged --rm tonistiigi/binfmt --install arm64- Wine cell always FAIL — pull the image manually once
(
docker pull scottyhardy/docker-wine) and inspectlogs/node<major>/run-sea-win-x64.logfor the actual crash. The EBADF-stdout gotcha is documented above. - SEA FAIL on Node 20 — expected; pkg enforces
host node ≥ 22for SEA. Error: UNEXPECTED-20/ silent exit (EXIT=4) — these are the tracked Node-22 Standard-mode regressions (issues #181 and #87). Not environment bugs — see Node 22 results below.- Stale binary hiding a regression — pkg does not hash its own source
into the output. After rebuilding pkg, delete
bin-node<major>/before re-running the matrix.
The script
The harness lives next to this SKILL.md as run-matrix.sh. Before
first run, set PKG_BIN to your pkg build and ensure nvm is sourced.
Quick reference of pkg flags touched
| Flag | Effect |
-t nodeNN-<os>-<arch> | Target triple |
--sea | Enhanced SEA mode |
--public-packages "*" --public | Disable V8 bytecode, include sources in plaintext — the cross-compile escape hatch for Standard mode |
--no-bytecode | Same effect for bytecode, but does not imply --public — use with --public-packages if you want consistent behavior |
--debug | Inject diagnostic bootstrap; enables DEBUG_PKG at runtime |
Notes for future maintainers
- Do not reuse cached binaries between matrix runs — delete
bin-node${major}/first if you rebuilt pkg. pkg does not hash its own source into the output, so stale binaries silently hide regressions. - If you add a new target to the matrix, also add a runner branch in
run_one(). Unknown targets fall through thecaseand count as pass. - If
scottyhardy/docker-winebecomes unavailable,tobix/wineis a smaller drop-in replacement — it has the same EBADF stdout gotcha. - Don't confuse "builds with warning" with "builds clean". The matrix
only records
OKwhen the binary file exists on disk; a warning likeFailed to make bytecode node22-arm64still produces a binary but it will crash at runtime. Runtime is the source of truth.
In this repository4 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- performances-compareSkill
- pkg-debugSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

