16 KiB
G-122 make.tcl host/target platform split: bake by target, not host personality
Status: achieved 2026-07-26 Scope: src/make.tcl (target-platform derivation; store folder, exe suffixing, kit naming, presence checks and process-sweep tooling keyed by target; platform-canon inline copy; zipfs-less zip-type assembly fallback); src/modules/punk/platform-999999.0a1.0.tm (stable canon tags for cygwin-family hosts, help platforms doc); src/runtime/mapvfs.config (per-entry target platform via minimal compatible extension - format ownership coordinated with G-024); bin/runtime// store tiers (as consumed; fixture tier for tests); src/tests/shell/testsuites/punkexe/ (characterization) Goal: make.tcl's kit surfaces (bake/bakelist/bin) operate on an explicit target platform rather than the driving tclsh's personality: any capable host (native windows tclsh, msys/cygwin-runtime tclsh, a FreeBSD host running linux binaries) addresses the same per-target runtime store, artifact naming and process handling for the kits it reports/bakes; non-native-personality runtime tiers (e.g third-party mingw64 builds served from punkbin or an alternative artifact server) are representable as first-class mapping entries with their own store tier; and zip-type kit assembly no longer requires zipfs in the driving tcl. Acceptance: an msys/cygwin-runtime tclsh driving bakelist/bake on windows reports - and for a changed kit, bakes and deploys - the same win32-x86_64 kit set with identical names and store addressing as a native tclsh, with the pre-deploy process sweep using windows tooling for win32 targets regardless of host personality; punk::platform and the make.tcl inline canon map MSYS_NT/MINGW64_NT/CYGWIN_NT/UCRT64-class hosts to stable documented tags (MSYSTEM-variance documented, no windows build number in the tag) and 'help platforms' lists them; a mapvfs entry declaring a non-default target platform lists in bakelist with its own tier's store presence and bakes to a correctly suffixed artifact, verified against a fixture store tier (a real third-party runtime is not required); a zipfs-less driving tcl (8.6) assembles a zip-type kit via runtime split + punk::zip::mkzip + concatenation (archive-start-relative offsets) and the artifact boots on a zipfs-capable runtime; the linuxulator expectation (a linux-personality tclsh on FreeBSD 14/15 addresses the linux-x86_64 store unchanged) is recorded here with verification deferred to the planned linux-kit-on-FreeBSD trial; native-windows behaviour stays characterization-stable (existing punkexe maketcl tests pass unchanged).
Context
Field session 2026-07-26: the scoop msys2 tclsh8.6 driving src/make.tcl bakelist on windows showed every configured kit as runtime=missing (and
deployed absent) from two compounding causes: the store folder derives from the
HOST's platform canon (observed mingw64-x86_64 under a MINGW64_NT os string -
note the tag follows the MSYSTEM environment, so the same binary can canonize
as msys/ucrt64/mingw64 in different shells), and the platform=unix branch
probes suffixless filenames - an assumption that is false for the entire
cygwin family, whose executables are .exe files (the msys tclsh itself is
tclsh8.6.exe). The interim self-diagnosis shipped in punkshell 0.24.1
(bakelist store-folder WARNING with the canon derivation + (FOLDER MISSING)
header mark; bake's no-runtimes exit note) tells the user what happened; this
goal makes the tooling actually cater for it.
The underlying defect is a conflation: make.tcl's scattered
$::tcl_platform(platform) eq "windows" tests mix three separable concerns -
host process semantics (cp vs cmd /c copy, ps/kill vs
tasklist/taskkill, path personality), target artifact naming (.exe
suffixing, kit/output names), and store addressing (bin/runtime/<canon>).
An msys host pulls them apart. The process sweep is the sharpest edge: msys
ps sees only msys-descendant processes, so a natively-launched kit exe is
invisible to the pre-deploy sweep (taskkill remains exec-able from msys).
Direction (user, 2026-07-26): even if msys never becomes a zig buildsuite target, third-party-built runtime tiers (e.g an msys2-packaged tclsh) served from punkbin or an alternative artifact server must be supportable as kit runtimes - REPRESENTATION (mapping entry + store tier) belongs here; publication/fetch is the flagged follow-on goal.
FreeBSD linuxulator parallel (user): a linux-personality tclsh running on
FreeBSD via the linux compat layer is expected to self-identify as linux and
address bin/runtime/linux-x86_64 correctly - the planned
linux-kit-on-FreeBSD-14/15 trial doubles as verification that target-keyed
stores follow runtime personality, not host OS identity.
Zip offsets (user correction, 2026-07-26): tcl zipfs kits mount with
archive-start-relative offsets - cat-style construction (split the original
runtime first, then append a freshly written zip) is valid; zipfs mkimg's
file-relative offset adjustment is one convention, not a mounting
requirement. The existing zipcat kit type is the in-repo proof, and its
machinery already has zipfs-less fallbacks.
Approach
- One
target_platformderivation: default from the host canon, with cygwin-family hosts on windows defaulting to targetwin32-x86_64; per-entry mapping override for non-default tiers (a minimal compatible extension of the current vfsconfig entry - e.g a fourth element - whose schema home is G-024's toml conversion; the parsed-model seam (G-121, achieved - see goals/archive/G-121-bakelist-selective-bake.md) keeps every consumer format-agnostic while the format moves). - Key store folder, suffixing, kit/artifact naming, presence checks and the process-sweep tool selection by TARGET; keep copy commands, path handling and prompt behaviour host-driven.
- punk::platform canon: explicit MSYS_NT/MINGW64_NT/CYGWIN_NT/UCRT64 mapping to stable documented tags with the MSYSTEM-variance caveat; the make.tcl inline canon copy stays in sync per its existing comment contract.
- zip-type fallback for zipfs-less driving tcls: reuse the zipcat machinery (extract_preamble split + punk::zip::mkzip + concatenation).
- Characterization against a fixture store tier so no real third-party runtime is required; the msys-hosted acceptance run uses the dev machine's msys tclsh where present (else recorded env-gated, per punkexe suite conventions).
Alternatives considered
- Per-site host patches (special-case each windows test) - rejected: the three-concern conflation just re-accumulates with each new host class.
- Full self-hosted msys platform lineage (msys-personality kits, accelerator builds, own test baselines) - out of scope: the third-party tier direction covers the need without a new first-party build lineage.
- Status quo (require a native tclsh to drive make.tcl) - rejected: field friction (the all-missing bakelist), and upcoming linux/FreeBSD hosts make host-personality assumptions untenable regardless of msys.
Notes
- Related: G-024 - owns the mapping format; the per-entry target platform is drafted as a minimal compatible extension and its schema home is that goal's toml conversion (make.tcl's own comments already pair the platform column with the toml change).
- Related: G-105 - cross-target zig builds populate first-party non-native store tiers; this goal is the consuming/addressing side.
- Related: G-114 - same punk::platform canon vocabulary, applied to per-platform tm module roots.
- Related: G-101 - 8.6 kit containers ride the same mapping/store surfaces; lister/bake stay kit-type-agnostic (per the archived G-121 decision).
- Related: G-057 - per-entry mapping extension precedent (per-vfs icon override rides the same entry shape).
- Related: G-023 - kit naming schemes; target-keyed naming must not collide with its version-named binaries direction.
- Follow-on: G-123 (drafted 2026-07-26, user-approved) - third-party runtime tier publication/fetch via punkbin or alternative servers, with schema-v2 provenance labeling and server-trust-keyed consent.
- Related: G-028 - the pre-deploy process sweep is the shared surface: this goal picks the sweep TOOLING by target (windows tooling for win32 targets from any host personality), that goal names the process actually holding a target the build cannot replace. A locker-report helper must make the same host-vs-target tooling choice. (activation-freshness survey, 2026-07-26)
- Related: G-060 - the eventual vehicle for exercising non-native target output: this goal makes cross-target kits addressable/bakeable from one host, a guest matrix is what verifies the artifacts actually boot (and is the natural home for the deferred linuxulator trial below). (activation-freshness survey, 2026-07-26)
- Out of scope: msys pty raw-mode console/repl behaviour - the cursor-row-1 field finding of 2026-07-26 is recorded in G-013's Notes.
- FreeBSD linuxulator expectation (recorded per the acceptance; verification
deferred to the planned linux-kit-on-FreeBSD-14/15 trial, G-060 being the
natural harness). A linux-personality tclsh running on FreeBSD via the linux
compat layer self-identifies as linux, so
platform_punkyieldslinux-x86_64,platform_target_defaultreturns it unchanged (the cygwin-family exception is keyed on the os token being msys/cygwin, which a linuxulator tclsh never reports), and the bake addressesbin/runtime/linux-x86_64with suffixless artifact names and posix (ps/kill) process tooling. Target-keyed stores follow RUNTIME personality, not host OS identity: that is the same rule that makes an msys host bake win32 kits, applied in the other direction. What the trial must actually check is thattcl_platform(os)reports Linux (not FreeBSD) under the compat layer - if it reported FreeBSD, the store addressed would bebin/runtime/freebsd-x86_64and the expectation would need revisiting. - Finding (2026-07-26, kept for whoever meets it next): a cygwin-family tclsh
is a POSIX Tcl -
C:/repo/.../src/make.tclis not an absolute path to it, sofile normalizeprefixes the cwd and the run dies in an unrecognisable cascade (missing bootsupport, then no project root). make.tcl now diagnoses exactly that shape and prints the/c/repo/...spelling to use. This is caller-side spelling, deliberately not rewritten by make.tcl. - Finding (2026-07-26): the zipfs-less EXTRACTION half depends on tcllib's
zipfile::decode, a SYSTEM package that punkshell does not vendor. msys2's/usr/bin/tclsh8.6has no tcllib, so a bake there cannot lift the runtime's own zip (itstcl_library) into the kit and the artifact will not boot; the bake now emits a recapped BUILD-WARNING saying so, and still deploys. With a tcllib on the path the same msys tclsh splits and decodes byte-identically to a native 8.6 host (verified: baseoffset 5267968, 1429659 zip bytes, 1784 entries from both). Candidate follow-ons flagged to the user rather than written as goals: (a) vendor or write a pure-Tcl zip decoder so the zipfs-less path is self-contained, (b) refuse to DEPLOY a kit whose extraction produced no tcl_library instead of warning past it. - Finding (2026-07-26): the
bin/punkshell902name is now producible on windows (cross-target bake), and several punkexe suites use it as the second-choicePUNK_SHELL_TEST_EXEcandidate afterbin/punk902z.exe. On a windows machine with no windows kit but a cross-baked linux one, that fallback would select an ELF binary. Left as-is (pre-existing candidate-list logic, narrow state);PUNK_SHELL_TEST_EXEoverrides it.
Progress
Achieved 2026-07-26 in one work unit. What landed and how each acceptance clause was verified:
- Target-platform derivation (
::punkboot::lib::platform_*insrc/make.tcl):platform_target_default(cygwin-family host -> win32),platform_store_tier(macosx-* -> macosx),platform_exe_suffix,platform_process_family,platform_name_problem, plusplatform_normalize_osmirroring the module. Globals::punkboot::host_platform/target_platform/host_windowsseparate the two axes;this_platform_genericstays the HOST key for bootsupport and vendorlib platform dirs. - Mapping model keyed by target:
mapvfs_parsetakes the store ROOT plus a default target and resolves each entry's own tier/suffix (4th element; conflicting or malformed targets are config errors);mapvfs_kit_outputsrecords carrytarget/store_tier/runtime_dir. The bake path parses the mapping BEFORE runtime discovery, since the mapping is what says which tiers to look in; cross-target runtimes are picked up only when named. - msys-host acceptance (msys2
/usr/bin/tclsh8.6, canonmsys-x86_64,tcl_platform(platform)unix):bakelistoutput is identical to the native Tcl 9.0.3 run (line endings aside) - same 10 kits, same names, sameruntimes: bin/runtime/win32-x86_64addressing,punk91detail resolvingbin/runtime/win32-x86_64/tclsfe-x64.exe (present),target: win32-x86_64 (host default). Before the split every row readruntime=missingagainst a nonexistentmingw64-x86_64store. A changedpunk91then baked and deployed from that host with a hiddenpunk91.exe(pid 10120) running: the sweep chose tasklist, FOUND it - msyspsreports 0 matches for the same process, measured side by side - killed it with taskkill and replacedbin/punk91.exe. - msys2 argument mangling (found by that run): msys2 rewrites arguments
that look like absolute posix paths when spawning a native windows program,
so
taskkill /PID 10120arrived astaskkill C:/<msysroot>/PID 10120and the kill failed the kit. Native windows command lines now go through::punkboot::exec_nativeargs(MSYS2_ARG_CONV_EXCL=*, inert on other hosts); the re-run killed and deployed cleanly. - Cross-target entry: the linux tclkit entry declares
linux-x86_64and is read frombin/runtime/linux-x86_64/with no.exe.bakelistlists it with atarget=linux-x86_64note and(present)store state;make.tcl bake punkshell902from a windows host produced a 24,200,032-byte ELFbin/punkshell902(sdx wrap over the linux runtime), with the process sweep correctly skipped as not-visible-from-this-host. A real cross-target runtime was used - the acceptance's fixture-tier allowance was not needed. - Zipfs-less zip assembly: native Tcl 8.6.17 (no zipfs) baked
punk91via raw-runtime split +punk::zip::mkzip+ concatenation (5,267,968 + 44,242,534 = 49,510,502 bytes) and the artifact BOOTS -//zipfs:/app/tcl_librarymounted, 4 zipfs mounts, punk::lib 0.5.1 loaded. Thezipcatkit type now shares the same::punkboot::assemble_zipcat_imagehelper. - Latent defects the split exposed and fixed: the runtime capability probe
tested a suffixless filename and so reported
find-failfor every runtime on windows (it now probes the target's own tier and suffix, and skips cross-target runtimes as unrunnable); with real capabilities arriving, the extraction try-list was found to test capability PRESENCE rather than value, which would send a plain zipfs runtime down the kit path where a failedsdx mksplitreplacesraw_runtimewith the un-split original - a silently unbootable kit; BUILDCOPY naming now follows the runtime's own filename; and runtime map keys strip only.exe(file rootnamewould eat the last dotted segment oftclsh9.0.5). - Characterization: new
src/tests/shell/testsuites/punkexe/maketclplatform.test(5 tests: thecheckderivation line, default-target detail, the cross-target entry's tier/target/suffixless naming, and two cygwin-host tests that self-gate on discovering and probing a real msys/cygwin tclsh - the derivation line and full bakelist row-set parity against the native run). Native behaviour stayed characterization-stable: full suite 1112 tests, 1093 passed, 18 skipped, 1 failed = the documentedexec-14.3baseline; the pre-existingmaketclbakelist/maketclcolourtests pass unchanged. - Reported surface:
make.tcl checkprintsplatform (G-122): host=... target=... store=... exe-suffix=... process-tooling=...;bakelistrows carrytarget=<platform>for non-default targets and the detail block names the tier and whether the target is the host default or declared; the workflow text gained key note [K9] and a target-keyed store line. punkshell 0.24.1 -> 0.25.0.