diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index fa81e6fe..18903239 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -83,7 +83,7 @@ Two layers with a deliberate dependency direction (the class never depends on th - **`src/make.tcl` is the single build entry.** Key subcommands: `modules`/`libs`/`packages` (repo-root build outputs), `vfscommonupdate` (regenerate `_vfscommon.vfs`), `bake` (kit assembly into `bin/`; optional kit names confine the run to those kits - G-121), `bakelist` (report the configured kit matrix with runtime/vfs presence and deployed state), `bin`, `bakehouse` (packages + bake for a clean checkout; `project`/`vfs` remain as deprecated aliases - G-112 rename, achieved), `bootsupport` (snapshot refresh), `vendorupdate`, `check`, `workflow`, `projectversion`. Dispatch and help dogfood `punk::args` (G-030, achieved) and degrade to plain scan/help when bootsupport `punk::args` is stale (`PUNKBOOT_PLAIN=1` forces the degraded mode). Source: `src/AGENTS.md`. - **punkcheck** records every install/delete as events in per-folder `.punkcheck` directories - the basis for skip/copy change detection, superseded-module pruning and provenance. Single OO record lifecycle (G-094, achieved) with atomic saves and advisory event-scoped locking for concurrent writers (G-095, achieved). - **Provenance gates.** Build/promotion commands warn on uncommitted `src/` changes (column-0 `PROVENANCE-WARNING:` token, `-dirty-abort` for strict mode); `vendorupdate` warns for dirty source-project checkouts. -- **Buildsuites and the kit family.** `src/buildsuites/suite_tcl90/` builds Tcl/Tk/tcllib from source with a pinned zig toolchain and produces the runtime kit family (plain / punk / bi) plus artifact metadata (G-096-G-117 era: see archived goals G-096, G-098, G-102, G-103, G-107); artifacts publish to the punkbin repo that `bin/punk-runtime.cmd` fetches from. `src/buildsuites/suite_tcl86/` is the 8.6 sibling (G-099, achieved): a Tcl 8.6 windows runtime (static + dynamic shells, `tcl86t.dll`, on-disk lib tree - no zipfs, so no self-contained kit) with thread 2.8 + tclvfs companions, gated against the 8.6 core/thread/tclvfs testsuites. The 8.6 dependency set (Tk/tcllib) and kit container strategy remain in-flux - see "In-flux areas". +- **Buildsuites and the kit family.** `src/buildsuites/suite_tcl90/` builds Tcl/Tk/tcllib from source with a pinned zig toolchain and produces the runtime kit family (plain / punk / bi) plus artifact metadata (G-096-G-117 era: see archived goals G-096, G-098, G-102, G-103, G-107); artifacts publish to the punkbin repo that `bin/punk-runtime.cmd` fetches from. `src/buildsuites/suite_tcl86/` is the 8.6 sibling (G-099 + G-100, both achieved): a Tcl 8.6 windows runtime (static + dynamic shells, `tcl86t.dll`, on-disk lib tree - no zipfs, so no self-contained kit) with thread 2.8 + tclvfs + Tk 8.6 + tklib + tcllib(+tcllibc critcl accelerators) companions, gated against the 8.6 core/thread/tclvfs testsuites with tcllib/tklib on a record tier and an opt-in `test-tk`; punkshell's own runtests is censused on that runtime at parity with a same-day native-8.6 baseline. Suite child shells scrub `TCL__TM_PATH` as well as TCLLIBPATH/TCL_LIBRARY/TK_LIBRARY - without it a census measures the machine's module trees. The 8.6 kit container strategy remains in-flux - see "In-flux areas". - **Project generation.** `dev project.new` composes thin layouts from `src/project_layouts/` (overlay chain with `.anti` deletion markers and `name@base` derivation - G-087, achieved) and injects bootsupport modules from the generating shell at generation time. - **Workflow overview.** `tclsh src/make.tcl workflow` prints the embedded ASCII data-flow overview of the build/release pipeline, with its own update contract in `src/AGENTS.md`. This section deliberately summarises rather than copies it. @@ -103,7 +103,7 @@ Mechanics here are documented as pointers only - detailed prose would be obsolet - Stored configuration and help: G-014 (punk::config toml), G-042 (subshell help topics), G-043 (subshell definition plugins). - Console err channel: G-011. - Launch surface: G-023 (version-named binaries), G-033 (`proj:` package mode), G-077 (`-e` one-liners), G-089 (scriptlib in kits). -- 8.6 buildsuite family: G-099 (core runtime, achieved 2026-07-26), G-100 (dependency set), G-101 (kit container). +- 8.6 buildsuite family: G-101 (kit container) - the core runtime (G-099) and the Tk/tklib/tcllib dependency set (G-100) are achieved 2026-07-26. ## Doc map - which lane holds what diff --git a/GOALS-archive.md b/GOALS-archive.md index 8e549928..02c27118 100644 --- a/GOALS-archive.md +++ b/GOALS-archive.md @@ -190,3 +190,7 @@ Acceptance: `make.tcl bakelist` under a plain tclsh and under a punk kit executa ### G-099 [achieved 2026-07-26] suite_tcl86: zig buildsuite for core-8-6-branch (runtime, thread, tclvfs, core-test gate) → detail: goals/archive/G-099-suite-tcl86-buildsuite.md Scope: src/buildsuites/suite_tcl86/ (new: suite.tcl driver, 8.6 zig recipe + helper modules, expected_test_failures baseline); src/buildsuites/_build/ (untracked sources/outputs via existing globs); bin/tools/ (pinned zig as consumed); PUNK_FOSSIL_STORE / ~/.punkshell/fossils (hermetic clone store shared with suite_tcl90) Acceptance: from the pinned zig and fetched core-8-6-branch sources (hermetic clone store, no MS toolchain) the suite produces a working tclsh86 executable and tcl86 dll with an installed lib tree the shell resolves without external Tcl installations (TCLLIBPATH/TCL_LIBRARY unset in all suite child invocations); an 8.6-compatible thread package and tclvfs build and load in that shell (versions recorded); suite.tcl test runs the 8.6 core testsuite, parses totals (exit codes not trusted), and gates against a tracked expected_test_failures baseline with every entry dispositioned with a reason - a full-run PASS deterministic across two consecutive runs is recorded in this file; an interactive-smoke record (script execution, package require checks) is captured here. + +### G-100 [achieved 2026-07-26] suite_tcl86 dependency set: Tk 8.6 + tklib, tcllib with critcl accelerators, punkshell runtests on the 8.6 runtime → detail: goals/archive/G-100-suite-tcl86-tk-tcllib.md +Scope: src/buildsuites/suite_tcl86/ (recipe extensions, driver steps); src/buildsuites/_build/ (tk/tklib/tcllib/critcl sources and outputs); bin/tools/ (pinned zig as consumed); src/tests/ (punkshell suites exercised against the suite-built 8.6 runtime) +Acceptance: from the pinned zig and fetched sources the suite produces a Tk 8.6 the suite tclsh loads (package require Tk with a create/destroy-window verification recorded here) and a matching tklib whose packages resolve (a representative require recorded); a tcllib install via its installer whose accelerators load (package require tcllibc) with the accelerated-vs-pure comparison recorded; punkshell runtests runs against the suite 8.6 runtime and its census is recorded here against a same-day native/kit 8.6 baseline run (same include set), with every difference dispositioned - Tk-dependent suites run unskipped, and any 8.6-runtime-only failures are root-caused or baselined with reasons. diff --git a/GOALS.md b/GOALS.md index 15b26988..0a02589e 100644 --- a/GOALS.md +++ b/GOALS.md @@ -349,10 +349,6 @@ Detail: goals/G-089-scriptlib-kits-and-modes.md Scope: src/modules/shellfilter-999999.0a1.0.tm (stack placement, insert_transform, junctions); src/modules/shellthread-999999.0a1.0.tm (worker park/adoption/termination); src/tests/modules/ (new characterization suites); README.md ('very unripe parts' note) Detail: goals/G-090-shellfilter-shellthread-audit.md -### G-100 [proposed] suite_tcl86 dependency set: Tk 8.6 + tklib, tcllib with critcl accelerators, punkshell runtests on the 8.6 runtime -Scope: src/buildsuites/suite_tcl86/ (recipe extensions, driver steps); src/buildsuites/_build/ (tk/tklib/tcllib/critcl sources and outputs); bin/tools/ (pinned zig as consumed); src/tests/ (punkshell suites exercised against the suite-built 8.6 runtime) -Detail: goals/G-100-suite-tcl86-tk-tcllib.md - ### G-101 [proposed] kit container strategy for zig-built Tcl 8.6 runtimes (no zipfs) Scope: investigation + decision record under src/buildsuites/suite_tcl86/ (or successor mechanism); TEMP_REFERENCE/metakit + TEMP_REFERENCE/KitCreator (read-only guidance); src/runtime/mapvfs.config ('kit' type consumers); src/make.tcl kit-wrap path (as consumer) Detail: goals/G-101-tcl86-kit-container-strategy.md diff --git a/goals/G-005-zig-build-infrastructure.md b/goals/G-005-zig-build-infrastructure.md index b32f4091..bd5c9055 100644 --- a/goals/G-005-zig-build-infrastructure.md +++ b/goals/G-005-zig-build-infrastructure.md @@ -51,7 +51,7 @@ This goal is the primary mechanism for G-004's outcome. It is independent of and respect - see goals/archive/G-058-static-runtime-packages.md. - Buildsuite-arc relationship (recorded 2026-07-24 after overlap review; neither this file nor the buildsuite goals previously referenced each other): the zig buildsuite - system - achieved G-096/G-098/G-099/G-102/G-103/G-104/G-107, live G-100/G-105/G-108/ + system - achieved G-096/G-098/G-099/G-100/G-102/G-103/G-104/G-107, live G-105/G-108/ G-116 - is the arm's-length runtime factory this goal's direction became, sharing the pinned-zig / zig-only toolchain policy. This goal's distinct remainder: building the VENDORED native extensions (twapi, tcllibc class) and wiring missing-binary retrieval diff --git a/goals/G-100-suite-tcl86-tk-tcllib.md b/goals/G-100-suite-tcl86-tk-tcllib.md deleted file mode 100644 index f0ff496b..00000000 --- a/goals/G-100-suite-tcl86-tk-tcllib.md +++ /dev/null @@ -1,57 +0,0 @@ -# G-100 suite_tcl86 dependency set: Tk 8.6 + tklib, tcllib with critcl accelerators, punkshell runtests on the 8.6 runtime - -Status: proposed -Scope: src/buildsuites/suite_tcl86/ (recipe extensions, driver steps); src/buildsuites/_build/ (tk/tklib/tcllib/critcl sources and outputs); bin/tools/ (pinned zig as consumed); src/tests/ (punkshell suites exercised against the suite-built 8.6 runtime) -Goal: suite_tcl86 builds/installs the remaining runtime pieces punkshell's Tcl 8 testing depends on - Tk from fossil core-8-6-branch loadable by the suite's 8.6 windows runtime together with a matching tklib (installer.tcl where it does more than a straight copy), and tcllib installed via its documented installer machinery WITH its critcl-built C accelerators (tcllibc; critcl driving zig cc, bootstrapped by the suite-built tclsh86 - no MS toolchain) - and punkshell's own runtests runs against the suite-built 8.6 runtime with Tk-dependent suites unskipped. -Acceptance: from the pinned zig and fetched sources the suite produces a Tk 8.6 the suite tclsh loads (package require Tk with a create/destroy-window verification recorded here) and a matching tklib whose packages resolve (a representative require recorded); a tcllib install via its installer whose accelerators load (package require tcllibc) with the accelerated-vs-pure comparison recorded; punkshell runtests runs against the suite 8.6 runtime and its census is recorded here against a same-day native/kit 8.6 baseline run (same include set), with every difference dispositioned - Tk-dependent suites run unskipped, and any 8.6-runtime-only failures are root-caused or baselined with reasons. - -## Context - -Mirror of G-098 (suite_tcl90 dependency set, achieved 2026-07-20) for the 8.6 arc. -Depends on G-099 (suite_tcl86 core buildsuite) - ACHIEVED 2026-07-26 (see -goals/archive/G-099-suite-tcl86-buildsuite.md); the 8.6 runtime (tclsh86ts/ -tclsh86t + tcl86t.dll + installed lib tree), thread 2.8.13, tclvfs 1.4.2 and -the core/thread/tclvfs test-gate this goal extends now exist, so the dependency -is met and G-100 is unblocked. - -8.6-specific expectations (verify at activation): - -- Tk core-8-6-branch pairs with the 8.6 runtime; the 9.0 build_tk zig module was - derived from tk9 makefile.vc object lists - the 8.6 lists/xlib arrangement differ, - so expect a re-derivation rather than reuse. -- tcllib trunk supports 8.5+; the same vendored critcl mechanism applies, with - critcl driven by the suite tclsh86. tcllibc built per-generation (the punk9wintk903 - vfs precedent carries tcl8/tcl9 tcllibc side by side under lib_tcl8/lib_tcl9). -- runtests under 8.6: no tcl::process, so -jobs child spawning falls back to - blocking execs which CONVOY on windows (documented) - run -singleproc 1 or accept - serialized child walls for the census run; record which mode produced the census. -- Census baseline: compare against the punk86-class kit runtime results on the same - day/include-set (forward-slash path conventions on 8.6 kits noted in project - memory) rather than against the 9.x census. -- The kit-child 'src' package_mode launch added to runtests for punk kits (G-098 - increment 7) applies to 8.6 punk kits too - a plain suite tclsh86 host takes the - native path. - -SEQUENCING: as for G-099 - proposed now, activation recommended after suite_tcl90's -G-096 (achieved 2026-07-20) remainder closed and punk905_beta trial concludes. - -## Notes - -- G-116 relationship (recorded 2026-07-24 after overlap review): G-116's zig-built - crypto-backend + static tcltls pattern (suite_tcl90) is the reference if the 8.6 - dependency set later grows a tls battery - the bi-family payload contract it - extends is generation-shaped, not 9-only. -- G-005 relationship: same zig toolchain-policy lineage as G-099's note - G-005 - owns the make.tcl-integrated vendored-extension build direction; the suites are - its arm's-length proving ground. -- G-099 (achieved, see goals/archive/G-099-suite-tcl86-buildsuite.md) delivered - the patterns this goal extends: the build86.zig recipe + build_tclthread86/ - build_tclvfs86 helpers (TEA-shape companion builds against the 8.6 stub lib), - the whole-tree library install + tm-module derivation, and the suite_tcl90 - G-107 (achieved - goals/archive/G-107-buildsuite-library-tests.md) policy-tier - test-gate (test_gate.tcl) - a Tk/tklib/tcllib gate follows the same shape. Two 8.6-specific traps recorded there carry forward: the - load-bearing MP_FIXED_CUTOFFS define and TCL_BROKEN_MAINARGS+console-subsystem - entry handling (build86.zig comments + README DERIVATION). Also inherited from - G-099 Notes: the G-106 powershell console-mode disableraw-via-server path is - only tcl9-verified - now that a twapi-less 8.6 runtime exists, run the G-106 - recipe against it (goals/archive/G-106-powershell-consolemode-fallback.md). diff --git a/goals/G-101-tcl86-kit-container-strategy.md b/goals/G-101-tcl86-kit-container-strategy.md index 9dceefae..2ab2350e 100644 --- a/goals/G-101-tcl86-kit-container-strategy.md +++ b/goals/G-101-tcl86-kit-container-strategy.md @@ -37,12 +37,24 @@ well-understood ports of the 9.0 work, while this is open investigation - keepin it out of their acceptance keeps the 8.6 suite arc incrementally landable. SEQUENCING: proposed; naturally last in the 8.6 arc (needs the G-099 runtime to -wrap). G-099 landed 2026-07-26 (achieved - see -goals/archive/G-099-suite-tcl86-buildsuite.md), so the runtime this goal wraps -now exists; activation still waits on G-100 (the dependency set) landing. +wrap). Both predecessors have landed - G-099 (achieved 2026-07-26 - see +goals/archive/G-099-suite-tcl86-buildsuite.md) built the runtime this goal wraps, +and G-100 (achieved 2026-07-26 - see goals/archive/G-100-suite-tcl86-tk-tcllib.md) +completed the dependency set that becomes this container's payload. The arc is +unblocked; activation is the user's call. ## Notes +- Related: G-116 (suite-built tcltls with a zig-built crypto backend) - the + payload question is shared: whatever container this goal picks carries the + battery set, and G-116's bi-family payload contract is generation-shaped, not + 9-only. Recorded 2026-07-26 at the G-100 archive sweep, where the pair's only + bridges (G-100, G-103) both left the live tier. +- Payload inventory as of 2026-07-26: G-100 (achieved - see + goals/archive/G-100-suite-tcl86-tk-tcllib.md) completed the 8.6 dependency set + this container would wrap - Tk 8.6.17 (tk86t.dll + lib/tk8.6), tklib 0.9, + tcllib 2.0 + tcllibc accelerators, alongside G-099's thread 2.8.13 / tclvfs + 1.4.2 / registry / dde. - 2026-07-21: the prebuilt runtimes this goal replaces (tclkit86bi.exe, tclkit-win64-dyn.exe) are Metakit tclkits - self-contained (Tcl + VFS embedded) yet DYNAMICALLY linked to libc. That dynamic libc is what lets them diff --git a/goals/G-105-buildsuite-cross-target.md b/goals/G-105-buildsuite-cross-target.md index d65c92bd..be924f77 100644 --- a/goals/G-105-buildsuite-cross-target.md +++ b/goals/G-105-buildsuite-cross-target.md @@ -33,8 +33,9 @@ INVERTED): musl-dynamic). Relationships: G-060 (qemu cross-platform matrix - WSL is the cheap first rung; -qemu covers what WSL cannot), G-099/G-100 (the 8.6 suite inherits the target -dimension), G-102 (achieved 2026-07-21 - driver shape settled; target becomes a build-graph dimension), G-103 (achieved 2026-07-22 - see goals/archive/G-103-runtime-kit-family.md) +qemu covers what WSL cannot), G-099/G-100 (both achieved 2026-07-26 - the 8.6 +suite and its Tk/tklib/tcllib dependency set inherit the target dimension; see +goals/archive/G-100-suite-tcl86-tk-tcllib.md), G-102 (achieved 2026-07-21 - driver shape settled; target becomes a build-graph dimension), G-103 (achieved 2026-07-22 - see goals/archive/G-103-runtime-kit-family.md) (family x target = the punkbin artifact matrix), G-006 (punkbin artifact consent). ## Notes diff --git a/goals/G-108-buildsuite-debug-tier.md b/goals/G-108-buildsuite-debug-tier.md index a02e691a..fd32aa07 100644 --- a/goals/G-108-buildsuite-debug-tier.md +++ b/goals/G-108-buildsuite-debug-tier.md @@ -55,6 +55,12 @@ TCL_COMPILE_STATS similarly must be absent (not =0) for release performance. ## Notes +- G-100 (archived) recorded a census-hygiene finding this goal's recorded test + runs depend on: `TCL__TM_PATH` env vars seed tm.tcl's module + roots and were NOT part of the suite scrub list, so a suite shell can silently + prefer machine/repo tm modules over what the suite installed. Fixed for + suite_tcl86; suite_tcl90 still has the exposure via `TCL9_0_TM_PATH` - see + goals/archive/G-100-suite-tcl86-tk-tcllib.md. - G-104 (archived) recorded this goal as a surface consumer: 'make.tcl buildsuite build' forwards driver args (-steps/-zigargs) untouched and list/info read the sources.config self-description records, so the -dbg diff --git a/goals/G-116-suite-built-tcltls.md b/goals/G-116-suite-built-tcltls.md index 7f113acb..3142af8c 100644 --- a/goals/G-116-suite-built-tcltls.md +++ b/goals/G-116-suite-built-tcltls.md @@ -44,6 +44,13 @@ extends). ## Notes +- G-100 (achieved 2026-07-26 - goals/archive/G-100-suite-tcl86-tk-tcllib.md) + recorded two things this goal will hit when it adds its own sources: zig + 0.16's `zig fetch` resolves a BUILD ROOT before fetching (run it from a + directory with a build.zig, e.g. the suite dir, or it dies with a misleading + "no build.zig file found"), and the suite env scrub must include + `TCL__TM_PATH` or a battery run measures the machine's module + trees instead of the suite's (suite_tcl90 still has that exposure). - G-117 relationship (G-117 achieved 2026-07-24 - see goals/archive/G-117-self-describing-runtimes.md; recorded 2026-07-24 after overlap review; neither file previously referenced the other): this goal's Acceptance extends the artifact diff --git a/goals/archive/G-100-suite-tcl86-tk-tcllib.md b/goals/archive/G-100-suite-tcl86-tk-tcllib.md new file mode 100644 index 00000000..9b925453 --- /dev/null +++ b/goals/archive/G-100-suite-tcl86-tk-tcllib.md @@ -0,0 +1,210 @@ +# G-100 suite_tcl86 dependency set: Tk 8.6 + tklib, tcllib with critcl accelerators, punkshell runtests on the 8.6 runtime + +Status: achieved 2026-07-26 +Scope: src/buildsuites/suite_tcl86/ (recipe extensions, driver steps); src/buildsuites/_build/ (tk/tklib/tcllib/critcl sources and outputs); bin/tools/ (pinned zig as consumed); src/tests/ (punkshell suites exercised against the suite-built 8.6 runtime) +Goal: suite_tcl86 builds/installs the remaining runtime pieces punkshell's Tcl 8 testing depends on - Tk from fossil core-8-6-branch loadable by the suite's 8.6 windows runtime together with a matching tklib (installer.tcl where it does more than a straight copy), and tcllib installed via its documented installer machinery WITH its critcl-built C accelerators (tcllibc; critcl driving zig cc, bootstrapped by the suite-built tclsh86 - no MS toolchain) - and punkshell's own runtests runs against the suite-built 8.6 runtime with Tk-dependent suites unskipped. +Acceptance: from the pinned zig and fetched sources the suite produces a Tk 8.6 the suite tclsh loads (package require Tk with a create/destroy-window verification recorded here) and a matching tklib whose packages resolve (a representative require recorded); a tcllib install via its installer whose accelerators load (package require tcllibc) with the accelerated-vs-pure comparison recorded; punkshell runtests runs against the suite 8.6 runtime and its census is recorded here against a same-day native/kit 8.6 baseline run (same include set), with every difference dispositioned - Tk-dependent suites run unskipped, and any 8.6-runtime-only failures are root-caused or baselined with reasons. + +## Context + +Mirror of G-098 (suite_tcl90 dependency set, achieved 2026-07-20) for the 8.6 arc. +Depends on G-099 (suite_tcl86 core buildsuite) - ACHIEVED 2026-07-26 (see +goals/archive/G-099-suite-tcl86-buildsuite.md); the 8.6 runtime (tclsh86ts/ +tclsh86t + tcl86t.dll + installed lib tree), thread 2.8.13, tclvfs 1.4.2 and +the core/thread/tclvfs test-gate this goal extends now exist, so the dependency +is met and G-100 is unblocked. + +8.6-specific expectations (verify at activation): + +- Tk core-8-6-branch pairs with the 8.6 runtime; the 9.0 build_tk zig module was + derived from tk9 makefile.vc object lists - the 8.6 lists/xlib arrangement differ, + so expect a re-derivation rather than reuse. +- tcllib trunk supports 8.5+; the same vendored critcl mechanism applies, with + critcl driven by the suite tclsh86. tcllibc built per-generation (the punk9wintk903 + vfs precedent carries tcl8/tcl9 tcllibc side by side under lib_tcl8/lib_tcl9). +- runtests under 8.6: no tcl::process, so -jobs child spawning falls back to + blocking execs which CONVOY on windows (documented) - run -singleproc 1 or accept + serialized child walls for the census run; record which mode produced the census. +- Census baseline: compare against the punk86-class kit runtime results on the same + day/include-set (forward-slash path conventions on 8.6 kits noted in project + memory) rather than against the 9.x census. +- The kit-child 'src' package_mode launch added to runtests for punk kits (G-098 + increment 7) applies to 8.6 punk kits too - a plain suite tclsh86 host takes the + native path. + +SEQUENCING: as for G-099 - proposed now, activation recommended after suite_tcl90's +G-096 (achieved 2026-07-20) remainder closed and punk905_beta trial concludes. + +## Progress + +### 2026-07-26 activation + increment 1 (dependency set built, census taken) + +- ACTIVATED by the user. Sources wired into both flows: `sources.config` gains + tk (`core-8-6-branch` -> dir `tk86`), tklib/tcllib (trunk) and critcl (git + 3.3.1); `build.zig.zon` gains the matching pins - tk freshly `zig fetch`ed + (checkin 9986fdc267, 2025-03-17 = Tk 8.6.17), tklib/tcllib/critcl re-fetched + and verified BYTE-IDENTICAL to suite_tcl90's pins (generation-neutral + upstreams; what differs per suite is the shell running the installers and the + critcl target). suite.tcl gains git-source support + the new staging items; + the bootstrap driver's `source_dirs`/`recipe_items` match. +- TK 8.6 RE-DERIVED (`build_tk86/build_tk86.zig`) from the staged 8.6 + `win/makefile.vc` + `rules.vc`, not ported from the 9.0 module - the goal + Context's expectation held. Full derivation record in + `src/buildsuites/suite_tcl86/README.md`; the load-bearing differences: + no `tkPkgConfig.c` (so NO `CFG_*` defines at all), the + TCL_MAJOR_VERSION==8 naming arm (`tk86t.dll`, `lib/tk8.6`), the + `@TK_WIN_VERSION@` = `$(DOTVERSION).0.0` manifest substitution (not the + tcl-style releaselevel formula), and an 8.6 object inventory without + tkIcu/tkImgListFormat/tkImgSVGnano/tkWinGDI/tkWinIco/tkWinSysTray. Products: + `bin/tk86t.dll` + `lib/tk86t.lib` + the `lib/tk8.6` script library + + makefile-shaped `pkgIndex.tcl`. +- TK ACCEPTANCE EVIDENCE (repeatable - `tools/tk_smoke.tcl` runs in the `smoke` + step on every build, and a load-only check would pass with no usable display): + `tk_smoke OK: Tk 8.6.17 (Ttk 8.6.17) windowingsystem win32 screen 3840x2160 + depth 32 widgets Label/TButton/Toplevel create+map+destroy clean`. Both shells + load it: `pkg_smoke OK: Thread:2.8.13 vfs:1.4.2 registry:1.3.5 dde:1.4.6 + Tk:8.6.17` (static AND dynamic). +- TKLIB via its OWN installer (batch, packages-only, explicit `-pkg-path` so the + installer's tcl_pkgPath-derived default can never place files off-tree) -> + `lib/tklib0.9`. Representative require recorded: `pkg_smoke OK: tooltip:2.0.4` + - a Tk-DEPENDENT package resolving through the installer-generated aggregate + pkgIndex. +- TCLLIB 2.0 installed the same way (`md5:2.0.9 sha1:2.0.5 cmdline:1.5.3`), and + TCLLIBC built by critcl 3.3.1 driving `zig cc` under the suite-built 8.6 shell + (tracked `critcl_zig.config`, target pinned `win32-x86_64-zig` - critcl + otherwise probes PATH and falls back to its builtin msvc target). Accelerator + evidence, both on every build: `pkg_smoke OK: tcllibc:2.0 md5-critcl-accel:1` + and the new `tools/accel_bench.tcl` - + `accel_bench OK: sha1 over 4198400 bytes - critcl 5.9ms vs pure-tcl 2661.4ms + = 452.3x (digests identical, sha1 2.0.5, tcllibc 2.0)`. The bench loads BOTH + implementations via `::sha1::LoadAccelerator` and switches with + `::sha1::SwitchTo`, so the comparison is in-process and the digests are + compared rather than assumed. +- TEST TIERS extended on the G-107 policy pattern: `test-tcllib` / `test-tklib` + (record tier, inside `test-libraries`) and the OPT-IN `test-tk` (excluded from + `test-libraries`; `winDialog.test`/`winMsgbox.test` excluded by default - + their message-injection automation blocks on a human under a batch shell). +- HERMETIC-ENVIRONMENT FINDING (see Notes): `TCL__TM_PATH` was + never scrubbed, so the suite shells were silently preferring repo-built tm + modules over the tcllib the suite had just installed. Fixed in all three + scrub sites before the census was taken. + +#### punkshell runtests census (2026-07-26, both runs same day, same include set) + +Mode: DEFAULT single-process sequential (`-singleproc 1`; no `-jobs` - 8.6 has +no `tcl::process`, so parallel child spawning re-convoys on windows, per the +goal Context). Identical invocation for both runtimes, full source-tree suite, +no include filter: ` src/tests/runtests.tcl -report json`. + +- BASELINE - native Tcl 8.6.17 (`C:/Users/sleek/AppData/Local/Apps/Tcl86`, + which carries Tk 8.6.17, Thread 2.8.12, tcllibc 2.0, twapi): 101 files, + Total 1003 / Passed 986 / Skipped 10 / Failed 7, 657s. Files failed: + `core/tcl/.../exec.test` (ERROR `invalid command name "tcl::build-info"` - a + 9-era core test under an 8.6 shell), `modules/punk/ns/.../nslist.test` (4), + `modules/punk/ns/.../nsprimitives.test` (2 - the `:::odd` namespace-lookup + class), `modules/punkcheck/.../concurrency.test` (1). +- SUITE RUNTIME - `out/bin/tclsh86ts.exe` (8.6.18, this suite's static shell): + 101 files, Total 1003 / Passed 987 / Skipped 10 / Failed 6, 601s. +- DIFFERENCE SET (authoritative, `scriptlib/developer/runtests_parity.tcl`): + exactly ONE - `punkcheck concurrency_two_writers_no_corruption` ERRORed in the + BASELINE run and PASSED on the suite runtime. Every other per-file status, + tally, failure identity, skip identity+reason and warning code is identical. + DISPOSITION: flake under full-suite load, not a runtime difference - the file + passes 6/6 in isolation on BOTH runtimes, twice each (verified same day). + ZERO suite-runtime-only failures; nothing needed baselining. +- TK-DEPENDENT SUITES RAN UNSKIPPED on the suite runtime: + `modules/punk/repl/testsuites/repl/consolebackends.test` 3/3 PASS (its + tkconsole backend drives a child repl on the suite-built Tk) and + `modules/opunk/console/testsuites/console/backends.test` 7 passed / 1 skipped. + That single skip is `backends_tkconsole_widget_size_and_eof`, whose + `tkavailable` constraint requires the `PUNK_TEST_TK` OPT-IN env var (not Tk + availability); it skips identically on the native baseline, and the opt-in's + own pre-existing quirk is recorded in G-098's increment 6 notes. +- Runtime-difference note (does not change any result): the suite runtime has no + twapi, so its runs open with `is_interactive cannot detect console ... without + twapi` and a `punk::du` slower-disk-operations warning that the native + baseline (twapi present) does not print. + +#### library test tiers - first censuses (2026-07-26, suite tclsh86ts.exe 8.6.18) + +Beyond the acceptance text; run because the goal Context asks the G-107 +policy-tier shape to follow for the new libraries. + +- thread (gate): 142 run / 126 passed / 16 skipped / 0 failed - GATE PASS + (baseline still empty; constraint-driven skips only). +- tklib (RECORD, new tier): 796 run / 684 passed / 112 skipped / 0 failed. Its + Tk-dependent suites exercise the suite-built Tk, and nothing failed. +- tcllib (RECORD, new tier): 64259 run / 61186 passed / 3032 skipped / 41 + failed - names in `out/testreports/tcllib.summary`. Dominant classes: + network-reaching suites (picoirc 20, httpd-proxy/cgi 4), installer/path + assumptions (install-2.1/2.2, find-sf-*/find-4.0, multi/multiop-5.2), plus + matrix-16.7/16.8, tar-add, uri-4.1, example-*, zipfile-encode-6.4/6.5. A + record tier is evidence, not a gate criterion - no baseline is tracked and the + step never fails the build on these. +- tclvfs (gate): the tcllib install CHANGED this suite's census - vfsFtp-1.1.. + 1.3 sit behind an `ftp` constraint that needs tcllib's ftp package (absent at + the G-099 capture: 3 constraint skips), so they now RUN and reach the retired + `ftp://ftp.tcl.tk`, the same external-service class already baselined for + vfsUrl-1.2/2.1 (skips 8 -> 5 accordingly). `expected_test_failures_tclvfs.txt` + extended with the three names and the cause; re-run confirms GATE PASS + (51 run / 42 passed / 5 skipped / 4 failed, all baselined; vfsUrl-1.2 noted as + a stale entry - which members of the family fail is network-timing dependent, + so stale notes are expected and never fatal). + +Remains for acceptance: nothing - the four acceptance clauses (Tk loadable + +create/destroy verification, matching tklib with a representative require, +tcllib installed by its own machinery with loading accelerators and a recorded +accelerated-vs-pure comparison, and a dispositioned runtests census against a +same-day 8.6 baseline with Tk suites unskipped) are all recorded above. + +## Notes + +- Hermetic-environment finding 2026-07-26 (fixed here, exposure recorded for + suite_tcl90): `scrubTclEnv` cleared only TCLLIBPATH/TCL_LIBRARY/TK_LIBRARY, + but `tm.tcl` also seeds module roots from `env(TCL__TM_PATH)` + down through every lower minor. This machine points `TCL8_6_TM_PATH` at + `C:/repo/jn/shellspy/modules` among others, so suite shells preferred a + repo-built `sha1-2.0.4.tm` / `md5 2.0.8` over the tcllib 2.0.5 / 2.0.9 the + suite had just installed - a census through such a shell measures the machine. + Scrub list now covers `TCL8_0..8_7_TM_PATH` + `TCL9_0/9_1_TM_PATH` in the + per-step scrub (`build_common.zig` `tcl_env_vars`), `suite.tcl`'s + process-level unset and the bootstrap nested-build scrub. suite_tcl90 has the + same exposure via `TCL9_0_TM_PATH` and was deliberately left untouched (out of + this goal's scope) - worth a suite_tcl90-scoped fix. +- zig 0.16 trap (recorded while pinning): `zig fetch ` resolves a BUILD + ROOT before fetching, so it must be run from a directory containing a + `build.zig`; from anywhere else it dies with the misleading "no build.zig file + found, in the current directory or any parent directories". Also in the + `build.zig.zon` header. +- Activation freshness 2026-07-26: goals_xref.tcl score G-100 re-run at the + proposed->active flip (user-directed activation). Top pairs G-105 / G-101 / + G-116 / G-108 reviewed; the two that were only one-directional get the + back-pointers below, G-116 was already recorded here, and the remaining hits + are term noise - no new relationships. +- Related: G-105 (buildsuite cross-target) - it names this arc as a target- + dimension consumer; the tk/tklib/tcllib/critcl steps this goal adds must not + preclude a per-target dimension (no per-target tree copies, per-target out + prefixes). +- Related: G-101 (8.6 kit container strategy) - the downstream consumer of this + dependency set: whatever container it picks carries the Tk/tklib/tcllib/ + tcllibc payload this goal produces. +- Related: G-108 (debug buildsuite tier) - sibling suite-arc goal queued behind + this arc; its -dbg/tcl::test variant pattern is suite_tcl90-scoped today. +- G-116 relationship (recorded 2026-07-24 after overlap review): G-116's zig-built + crypto-backend + static tcltls pattern (suite_tcl90) is the reference if the 8.6 + dependency set later grows a tls battery - the bi-family payload contract it + extends is generation-shaped, not 9-only. +- G-005 relationship: same zig toolchain-policy lineage as G-099's note - G-005 + owns the make.tcl-integrated vendored-extension build direction; the suites are + its arm's-length proving ground. +- G-099 (achieved, see goals/archive/G-099-suite-tcl86-buildsuite.md) delivered + the patterns this goal extends: the build86.zig recipe + build_tclthread86/ + build_tclvfs86 helpers (TEA-shape companion builds against the 8.6 stub lib), + the whole-tree library install + tm-module derivation, and the suite_tcl90 + G-107 (achieved - goals/archive/G-107-buildsuite-library-tests.md) policy-tier + test-gate (test_gate.tcl) - a Tk/tklib/tcllib gate follows the same shape. Two 8.6-specific traps recorded there carry forward: the + load-bearing MP_FIXED_CUTOFFS define and TCL_BROKEN_MAINARGS+console-subsystem + entry handling (build86.zig comments + README DERIVATION). Also inherited from + G-099 Notes: the G-106 powershell console-mode disableraw-via-server path is + only tcl9-verified - now that a twapi-less 8.6 runtime exists, run the G-106 + recipe against it (goals/archive/G-106-powershell-consolemode-fallback.md). diff --git a/src/buildsuites/AGENTS.md b/src/buildsuites/AGENTS.md index f3bb8d08..f8122a93 100644 --- a/src/buildsuites/AGENTS.md +++ b/src/buildsuites/AGENTS.md @@ -40,8 +40,11 @@ this project's `src/` tree. - `suite_tcl90/` - Tcl 9.0 suite (its README.md carries lineage/detail; no child AGENTS.md yet) -- `suite_tcl86/` - Tcl 8.6 suite, G-099 arc (recipe landed 2026-07-26: - tclsh86ts/tclsh86t + tcl86t.dll + lib tree, thread 2.8.13 + tclvfs 1.4.2, - smokes green; test censuses/baselines in progress - its README.md carries - the deliverables, source selections and the makefile.vc DERIVATION record; - no child AGENTS.md yet) +- `suite_tcl86/` - Tcl 8.6 suite (G-099 achieved 2026-07-26: tclsh86ts/tclsh86t + + tcl86t.dll + lib tree, thread 2.8.13 + tclvfs 1.4.2, core/thread/tclvfs + gates; G-100 active: Tk 8.6.17 + tklib 0.9 + tcllib 2.0/tcllibc, and + punkshell's own runtests censused on this runtime). Its README.md carries the + deliverables, source selections, both makefile.vc DERIVATION records and the + hermetic-environment finding (TCL__TM_PATH must be scrubbed + alongside TCLLIBPATH/TCL_LIBRARY or a census measures the machine); no child + AGENTS.md yet diff --git a/src/buildsuites/suite_tcl86/README.md b/src/buildsuites/suite_tcl86/README.md index 6ac816d7..dea23621 100644 --- a/src/buildsuites/suite_tcl86/README.md +++ b/src/buildsuites/suite_tcl86/README.md @@ -1,18 +1,18 @@ -# suite_tcl86 - zig buildsuite for Tcl core-8-6-branch (G-099) +# suite_tcl86 - zig buildsuite for Tcl core-8-6-branch (G-099, G-100) Builds a Tcl 8.6 windows runtime from fossil core-8-6-branch sources with the pinned zig toolchain only (zig-only build policy; msys/mingw and MS toolchains -remain test/validation-only), together with the core binary companions -punkshell's Tcl 8 targets need - thread 2.8 and tclvfs - gated against the 8.6 -core testsuite. Forked from `suite_tcl90` (the shape reference); shared -scaffolding stays duplicated-with-a-note until this second consumer makes the -right factoring obvious (G-099 Context). - -STATUS: recipe LANDED 2026-07-26 - full `suite.tcl build` PASS (build 8.6.18 + -install + install-libraries + smokes on both shells, hermetic resolution -proven). Core/thread/tclvfs test censuses + dispositioned baselines are the -remaining G-099 acceptance items. Progress ledger: -`goals/G-099-suite-tcl86-buildsuite.md`. +remain test/validation-only), together with the binary and script companions +punkshell's Tcl 8 targets need - thread 2.8, tclvfs, Tk 8.6, tklib, and tcllib +with its critcl-built accelerators - gated against the 8.6 core testsuite. +Forked from `suite_tcl90` (the shape reference); shared scaffolding stays +duplicated-with-a-note until this second consumer makes the right factoring +obvious (G-099 Context). + +STATUS: G-099 ACHIEVED 2026-07-26 (runtime + thread/tclvfs + core-test gate - +see `goals/archive/G-099-suite-tcl86-buildsuite.md`). G-100 (the dependency set: +Tk + tklib + tcllib/tcllibc, and punkshell's own runtests on this runtime) is +ACTIVE - see `goals/G-100-suite-tcl86-tk-tcllib.md` for the progress ledger. ## Deliverables (per the G-099 acceptance; stock threaded-release naming) @@ -33,11 +33,38 @@ remaining G-099 acceptance items. Progress ledger: library pkgIndex files (http 2.9.9, msgcat 1.6.1, tcltest 2.5.11, platform 1.1.1, platform::shell 1.1.4). - `suite.tcl test` runs the 8.6 core testsuite, parses totals (exit codes not - trusted), and gates against a tracked `expected_test_failures.txt` baseline - - the FIRST CENSUS is in flight (record mode; the 9.0.5 census does not - transfer; machine traits dispositioned there will likely reappear under 8.6 - test names). Same census flow for the thread/tclvfs suites - (`expected_test_failures_thread.txt` / `_tclvfs.txt`). + trusted), and gates against the tracked dispositioned `expected_test_failures.txt` + baseline (2 entries: the windows admin-share self-globbing machine trait). + Same flow for the thread/tclvfs suites (`expected_test_failures_thread.txt` + empty / `_tclvfs.txt` 2 external-FTP entries). + +## Deliverables (G-100 dependency set) + +- Tk **8.6.17** from `core-8-6-branch`: `bin/tk86t.dll` (+ `lib/tk86t.lib` + import lib) and the script library at `lib/tk8.6` with the makefile-shaped + `pkgIndex.tcl` - `package require Tk` loads in BOTH shells, and the `smoke` + step's `tools/tk_smoke.tcl` maps a real toplevel with classic + themed + widgets, updates, and destroys it (a load-only check would pass with no + usable display). +- tklib **0.9** installed by its OWN `installer.tcl` (batch, packages only, + explicit `-pkg-path`): not a straight copy - module exclusions, app-script + generation and the aggregating top-level `pkgIndex.tcl` that makes the + two-levels-down module indexes resolvable. Smoke: `package require tooltip` + (a Tk-DEPENDENT tklib package) through that aggregate index. +- tcllib **2.0** installed by its own installer (same family; its critcl DSL + procs are stubbed to no-ops there - that is the pure-tcl side), plus + **tcllibc** built by critcl 3.3.1 driving `zig cc` (`critcl_zig.config`, + target pinned `win32-x86_64-zig`) under the suite-built 8.6 shell. Evidence + on every build: `pkg_smoke -accel md5 tcllibc` (asserts + `::md5::accel(critcl)`) and `tools/accel_bench.tcl` - sha1 over 4MB, + **critcl 5.9ms vs pure-tcl 2661ms = ~450x**, digests identical. +- Library test tiers extended (G-107 policy pattern): `test-tcllib` / + `test-tklib` on the RECORD tier inside `test-libraries`, plus the OPT-IN + `test-tk` (excluded from `test-libraries`: it maps windows on the interactive + desktop for tens of minutes; the native-dialog suites `winDialog.test` / + `winMsgbox.test` are excluded by default because their message-injection + automation blocks waiting on a human under a batch shell - re-include with + `-Dtestnotfiles-tk=""`). ## Sources (both flows carry the same identity) @@ -59,6 +86,19 @@ Selections (2026-07-25, staged and verified): (1.5.0) is tcl9-only (user decision 2026-07-22, recorded in suite_tcl90's sources.config). +Selections (2026-07-26, G-100 dependency set): + +- tk: `core-8-6-branch` -> dir `tk86` - tip checkin 9986fdc267 (2025-03-17) = + **8.6.17** (TK_PATCH_LEVEL in the staged tree). The 8.6 line is quiet + upstream; it pairs with the 8.6.18 core without complaint. +- tklib / tcllib / critcl: GENERATION-NEUTRAL - the same trunk refs and the + same content-hashed zon pins suite_tcl90 carries (re-fetched 2026-07-26 and + verified byte-identical). What differs per suite is the SHELL that runs the + installers and the critcl target that builds tcllibc; critcl compiles against + its own bundled headers, chosen by the version of the shell running it + (`lib/critcl/critcl_c/tcl8.6/` here), which is why one config file serves + both generations. + ## DERIVATION (build86.zig from the staged 8.6.18 win/makefile.vc) Recorded findings from the staged tree (assess-not-assume items from the G-099 @@ -112,12 +152,78 @@ tclvfs test gates. Two extra findings surfaced during the build (recorded in the DERIVATION list above): the load-bearing MP_FIXED_CUTOFFS, and the vfs142t.dll TEA name that lets Tcl's load-time name guessing derive Vfs_Init. +## DERIVATION (build_tk86 from the staged tk 8.6 win/makefile.vc) + +A RE-DERIVATION, not a port of suite_tcl90's `build_tk.zig` (the G-100 Context +predicted this). Verified against the staged tree 2026-07-26: + +- Object inventory: 8.6 `TKOBJS` has no `tkIcu` / `tkImgListFormat` / + `tkImgSVGnano` / `tkPkgConfig`, and no `tkWinGDI` / `tkWinIco` / + `tkWinSysTray`. `XLIBOBJS` is the same 5-file X emulation set but is a + first-class makefile macro here (in the 9.0 derivation those files had to be + recovered by hand after a name-pattern extraction missed them). Every name in + the staged makefile resolved to exactly one source dir - the recipe's lists + are that mapping. +- NO `generic/tkPkgConfig.c` in 8.6 -> the recipe passes no `CFG_*` defines at + all (the 9.0 module carries a full `CFG_{RUNTIME,INSTALL}_*` set for it). +- Naming: `rules.vc` TCL_MAJOR_VERSION==8 arm gives `TKLIBNAME` = **tk86t.dll** + (SUFX `t` survives while TCL_VERSION <= 86); the 9.0 arm produces the + `tcl9tk90.dll` form. Script install dir is `lib/tk8.6`. +- `wish.exe.manifest`: 8.6's makefile substitutes `@TK_WIN_VERSION@` with + `$(DOTVERSION).0.0` = "8.6.0.0" - NOT the tcl-style + `..` formula (`common.winResourceVersion`) + the 9.0 tree uses. `win/rc/tk.rc` embeds the manifest as RT_MANIFEST in the + DLL itself (that is what enables comctl32 v6 themed scrollbars), so it is + generated into the build cache and delivered through the rc include paths. +- `tkUuid.h`: 8.6 DOES generate it (`win/tkUuid.h.in` + `manifest.uuid`, + makefile.vc:541) and `tkWindow.c` includes it - same overlay treatment as the + tcl side. +- Stubs: `rules.vc` sets `USE_STUBS_DEFS` (`/DUSE_TCL_STUBS /DUSE_TCLOO_STUBS`) + for every project except tcl itself, so building Tk against the suite's + `libtclstub86.a` IS the stock arrangement - and it keeps the dll unbound to a + particular tcl dll name, so both the static and the dynamic suite shell load it. +- `tkMain.c` compiled twice (`tkMain.obj` + `tkMain2.obj` with UNICODE). The + ansi copy is forced with `-UUNICODE -U_UNICODE`; verified 2026-07-26 that the + pinned zig cc defines NEITHER for `x86_64-windows-gnu`, so the stock + ANSI/TCHAR arrangement of the 8.6 core is matched exactly (TCHAR resolution in + 8.6 depends on `_UNICODE` at each TU's compile - tk and tcl must agree). +- Deviation from stock, recorded: the generated `pkgIndex.tcl` keeps only the + windows arm of the makefile's two-branch text (the unix DISPLAY branch cannot + apply to a windows-only suite) and wraps the load path in `file normalize` + (as the 9.0 recipe does). + +## Hermetic environment (G-100 finding, 2026-07-26) + +`scrubTclEnv` cleared only TCLLIBPATH/TCL_LIBRARY/TK_LIBRARY. `tm.tcl` ALSO +seeds its module roots from `env(TCL__TM_PATH)` - for an 8.6 +shell, `TCL8_6_TM_PATH` down through every lower minor - and this dev machine +points `TCL8_6_TM_PATH` at repo/foreign module trees. A suite shell therefore +preferred a repo-built `sha1-2.0.4.tm` over the tcllib 2.0.5 the suite had just +installed (and `md5 2.0.8` over 2.0.9): any census taken through such a shell +measures the machine, not the suite. The scrub list now covers +`TCL8_0..8_7_TM_PATH` and `TCL9_0/9_1_TM_PATH` in all three places - the +per-step scrub (`build_common.zig` `tcl_env_vars`), `suite.tcl`'s process-level +unset, and the bootstrap driver's nested-build scrub. Keep the two lists in +step. NOTE: suite_tcl90 has the same exposure via `TCL9_0_TM_PATH` and is +deliberately untouched here (out of this goal's scope). + ## Flows - FOSSIL DEV FLOW: `tclsh suite.tcl build|test|clean` (or via the make.tcl surface: `tclsh src/make.tcl buildsuite build suite_tcl86 ?driver-args?`). -- NO-TCLSH FLOW: `zig build stage|bootstrap` - lands with the recipe; - `build.zig.zon` pins are already recorded. + Default build steps: `install install-libraries smoke tklib tcllib tcllibc`. +- NO-TCLSH FLOW: `zig build --build-file build86.zig stage|bootstrap` from THIS + tracked directory (materializes the `build.zig.zon` pins for any source that + has no checkout in the stage, then drives the staged recipe). `stage` verified + 2026-07-26 - it skips the existing fossil/git checkouts and stages the recipe + items (including `build_tk86/` and `critcl_zig.config`). + - FINGERPRINT TRAP (fixed 2026-07-26): `build.zig.zon`'s `.fingerprint` low + half is a checksum OF THE PACKAGE NAME, so a hand-authored value is rejected + at configure time ("invalid fingerprint ... if this is a new or forked + package, use this value: ..."). The G-099-era value was hand-authored and + survived unnoticed because the staged `suite.tcl` flow never reads this file + - the first real `zig build` from the tracked dir surfaced it. If you fork + this suite, take the value zig prints rather than inventing one. The suite self-description contract (records in `sources.config` read by `make.tcl buildsuite list/info`) is documented in `src/buildsuites/README.md`. diff --git a/src/buildsuites/suite_tcl86/build.zig.zon b/src/buildsuites/suite_tcl86/build.zig.zon index 483263e2..aab23274 100644 --- a/src/buildsuites/suite_tcl86/build.zig.zon +++ b/src/buildsuites/suite_tcl86/build.zig.zon @@ -5,16 +5,23 @@ //tarball urls + zig package hashes; 'zig fetch ' prints a hash, branch-tip //urls are deliberately NOT used - the pin IS the provenance record). // -//The bootstrap build.zig driver lands with the recipe (build86.zig is a -//pending-derivation skeleton); these records pin the 8.6 sources from the -//suite's FIRST COMMIT so both flows carry the same source identity +//These records pin the 8.6 sources so both flows carry the same source identity //(sources.config = live dev-flow refs on the same repositories/branches). -//Hashes below were computed with the pinned zig 0.16.0 ('zig fetch') on -//2026-07-25. +//Hashes computed with the pinned zig 0.16.0: the core set 2026-07-25, the +//G-100 dependency set 2026-07-26. TRAP: 0.16's 'zig fetch' resolves a build +//root BEFORE fetching, so it must be run from a directory with a build.zig +//(this suite dir does) - from anywhere else it dies with the misleading +//"no build.zig file found, in the current directory or any parent directories". .{ .name = .suite_tcl86, .version = "0.0.1", - .fingerprint = 0x9fe14fa771ae8617, + //zig-computed for the package name .suite_tcl86 - its low half is a checksum + //OF THAT NAME, so a hand-authored value is rejected at configure time + //('invalid fingerprint ... use this value: ...'). Corrected 2026-07-26 after + //the first real 'zig build --build-file build86.zig' from this tracked dir + //(the staged suite.tcl flow never reads this file, which is why the G-099-era + //hand-authored value survived unnoticed). + .fingerprint = 0x5c82bff30d9af9ad, .minimum_zig_version = "0.16.0", .dependencies = .{ //tcl core-8-6-branch checkout 1d1d5cbd91 2026-07-14 (8.6.17+ maintenance era) @@ -37,6 +44,35 @@ .hash = "N-V-__8AAIaeDQBJ9V3M96hpQSxemxghwLBG8fB1s0rqh4SQ", .lazy = true, }, + //-- G-100 dependency set ------------------------------------------ + //tk core-8-6-branch checkout 9986fdc267 2025-03-17 (8.6 maintenance line, + //pairing with the 8.6 runtime; the 9.0 suite pins core-9-0-branch instead) + .tk = .{ + .url = "https://core.tcl-lang.org/tk/tarball/9986fdc26786cecc8d513e05373fce5bd6d7b2377d8e3b6d17fcb8f09b2d81fb/tk.tar.gz", + .hash = "N-V-__8AANjEKQE7NM2LsFNsAzQl7mtH8R82KTFHnPrCkBuH", + .lazy = true, + }, + //tklib/tcllib/critcl are generation-neutral: the SAME pins suite_tcl90 + //carries (verified identical by re-fetch 2026-07-26). What differs per + //suite is the shell that runs the installers and the critcl target. + //tklib trunk checkout 2d53beafc0 2026-07-03 (0.9 era) + .tklib = .{ + .url = "https://core.tcl-lang.org/tklib/tarball/2d53beafc0e8621dacc75b3c809898cc9f1f92f9/tklib.tar.gz", + .hash = "N-V-__8AABomLAFd2U5OW96AfpQLoLm3Q9swpQZEwslrEJx3", + .lazy = true, + }, + //tcllib trunk checkout c784f6fda4 2026-06-30 (2.0 era) + .tcllib = .{ + .url = "https://core.tcl-lang.org/tcllib/tarball/c784f6fda4516f676453a2785b3c56f1ca5999f4/tcllib.tar.gz", + .hash = "N-V-__8AAFvlBARw4fIY107nOJmfxdwAjr1Vr89cYx98czpF", + .lazy = true, + }, + //critcl 3.3.1 (tag commit 254bdffe2a) + .critcl = .{ + .url = "https://github.com/andreas-kupries/critcl/archive/254bdffe2afc78148f79f92df8d6823a24f971bf.tar.gz", + .hash = "N-V-__8AAF8-bABDlxChmtnyalKgofVfN3kzcZdmZ7pjEmE0", + .lazy = true, + }, }, .paths = .{""}, } diff --git a/src/buildsuites/suite_tcl86/build86.zig b/src/buildsuites/suite_tcl86/build86.zig index 193da51a..07bf240f 100644 --- a/src/buildsuites/suite_tcl86/build86.zig +++ b/src/buildsuites/suite_tcl86/build86.zig @@ -56,6 +56,7 @@ const common = @import("build_common.zig"); const build_zlib86 = @import("build_zlib86/build_zlib86.zig").build_zlib86; const build_tclthread86 = @import("build_tclthread86/build_tclthread86.zig").build_tclthread86; const tclvfs86 = @import("build_tclvfs86/build_tclvfs86.zig"); +const tk86 = @import("build_tk86/build_tk86.zig"); const tcl_dot_version = "8.6"; const tcl_nodot_version = "86"; @@ -616,6 +617,12 @@ pub fn build(b: *std.Build) !void { build_tclvfs_step.dependOn(&tclvfs_compile.step); try tclvfs86.install_tclvfs_package("../tclvfs", b, tclvfs_compile); + //-- Tk 8.6 (G-100): tk86t.dll + the tk script library + pkgIndex, all hooked + //onto the default install step inside the helper (as the companions above). + const tk_build = try tk86.build_tk86(tcl_source_folder, "../tk86", b, target, optimize, finalstublib); + const build_tk_step = b.step("build-tk", "build the Tk 8.6 loadable extension (tk86t.dll) + tk script library install"); + build_tk_step.dependOn(&tk_build.lib.step); + // ================================================================ //install-libraries: the whole script library tree -> lib/tcl8.6 (each //subdir package keeps its own pkgIndex; encoding/tzdata/msgs ride inside), @@ -682,18 +689,168 @@ pub fn build(b: *std.Build) !void { //package-resolution smoke: the built companions + the library-dir //packages load in each shell (Thread/vfs via lib/, registry/dde via //the script library's package dirs - static registrations in the - //static shell, the built dlls in the dynamic one) + //static shell, the built dlls in the dynamic one; Tk via lib/tk8.6 + //under a withdrawn root, so no window flashes here) const pkgs = b.addSystemCommand(&.{sr.exe}); common.scrubTclEnv(pkgs); pkgs.has_side_effects = true; pkgs.setName(b.fmt("pkg_smoke {s}", .{sr.label})); pkgs.addFileArg(b.path("tools/pkg_smoke.tcl")); - pkgs.addArgs(&.{ "Thread", "vfs", "registry", "dde" }); + pkgs.addArgs(&.{ "-gui", "1", "Thread", "vfs", "registry", "dde", "Tk" }); pkgs.step.dependOn(b.getInstallStep()); pkgs.step.dependOn(install_libraries); smoke_step.dependOn(&pkgs.step); } + //-- Tk window-station smoke (G-100 acceptance evidence): maps a real + //toplevel with classic+themed widgets, updates, then destroys - a + //load-only check would pass without a usable display. Static shell only: + //the pkg_smoke runs above already prove Tk LOADS in both. + const tksmoke = b.addSystemCommand(&.{installed_static}); + common.scrubTclEnv(tksmoke); + tksmoke.has_side_effects = true; + tksmoke.setName("tk_smoke static"); + tksmoke.addFileArg(b.path("tools/tk_smoke.tcl")); + tksmoke.addArgs(&.{ "-expect", tk_build.patchlevel }); + tksmoke.step.dependOn(b.getInstallStep()); + tksmoke.step.dependOn(install_libraries); + smoke_step.dependOn(&tksmoke.step); + + // ============ G-100 script-library dependency set ============ + //Two shell roles here, deliberately split: + // INSTALLER/CRITCL runs use the build-cache artifact (addRunArtifact) so + // the step is CACHED on the shell's own hash and can own output dirs. + // That exe has no ../lib/tcl8.6 beside it, so those runs (and only + // those) get TCL_LIBRARY pointed at the staged source library. + // SMOKE runs use the INSTALLED shell with no TCL_LIBRARY, exactly as the + // smokes above: package resolution then comes from /lib via the + // exe-relative auto_path entry - which is also what proves the install + // layout, not just the build. + const tcl_library_src = common.replaceAll(b, b.pathFromRoot(tcl_source_folder ++ "/library"), "\\", "/"); + + //-- tklib: pure-tcl, but its installer is NOT a straight copy (module + //exclusion list, app-script generation, and generation of the aggregating + //top-level pkgIndex.tcl that makes the two-levels-down module indexes + //resolvable). Batch mode, packages only; -pkg-path is a cached output dir + //installed into the prefix, so the installer's own default (derived from + //the RUNNING shell's tcl_pkgPath) can never place files off-tree. + const tklib_ver = common.installerPackageVersion(b, "../tklib"); + const tklib_step = b.step("tklib", "install tklib via its own installer under the suite-built 8.6 shell (+Tk-dependent package smoke)"); + const tklib_run = b.addRunArtifact(tclsh_static_exe); + common.scrubTclEnv(tklib_run); + tklib_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src); + //cached (non-side-effect) Runs spawn with stdout IGNORED - on windows the + //child then has no stdout handle and tcl errors on any puts ('can not find + //channel named "stdout"'). Capturing gives it a real pipe. + _ = tklib_run.captureStdOut(.{}); + tklib_run.setCwd(b.path("../tklib")); + tklib_run.addFileArg(b.path("../tklib/installer.tcl")); + tklib_run.addArgs(&.{ "-no-gui", "-no-wait", "-no-apps", "-no-html", "-no-nroff", "-no-examples", "-pkg-path" }); + const tklib_out = tklib_run.addOutputDirectoryArg("tklib-pkg"); + const tklib_install = b.addInstallDirectory(.{ + .source_dir = tklib_out, + .install_dir = .prefix, + .install_subdir = b.fmt("lib/tklib{s}", .{tklib_ver}), + }); + const tklib_smoke = b.addSystemCommand(&.{installed_static}); + common.scrubTclEnv(tklib_smoke); + tklib_smoke.has_side_effects = true; + tklib_smoke.setName("tklib pkg_smoke"); + tklib_smoke.addFileArg(b.path("tools/pkg_smoke.tcl")); + tklib_smoke.addArgs(&.{ "-gui", "1", "tooltip" }); //a Tk-DEPENDENT tklib package + tklib_smoke.step.dependOn(&tklib_install.step); + tklib_smoke.step.dependOn(b.getInstallStep()); //Tk dll + pkgIndex + script library + tklib_smoke.step.dependOn(install_libraries); + tklib_step.dependOn(&tklib_smoke.step); + + //-- tcllib: same installer family (its critcl DSL procs are stubbed to + //no-ops there - this is the PURE-TCL side; the accelerators are the + //tcllibc step below). + const tcllib_ver = common.installerPackageVersion(b, "../tcllib"); + const tcllib_step = b.step("tcllib", "install tcllib (pure-tcl side) via its own installer under the suite-built 8.6 shell (+smoke)"); + const tcllib_run = b.addRunArtifact(tclsh_static_exe); + common.scrubTclEnv(tcllib_run); + tcllib_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src); + _ = tcllib_run.captureStdOut(.{}); + tcllib_run.setCwd(b.path("../tcllib")); + tcllib_run.addFileArg(b.path("../tcllib/installer.tcl")); + tcllib_run.addArgs(&.{ "-no-gui", "-no-wait", "-no-apps", "-no-html", "-no-nroff", "-no-examples", "-pkg-path" }); + const tcllib_out = tcllib_run.addOutputDirectoryArg("tcllib-pkg"); + const tcllib_install = b.addInstallDirectory(.{ + .source_dir = tcllib_out, + .install_dir = .prefix, + .install_subdir = b.fmt("lib/tcllib{s}", .{tcllib_ver}), + }); + const tcllib_smoke = b.addSystemCommand(&.{installed_static}); + common.scrubTclEnv(tcllib_smoke); + tcllib_smoke.has_side_effects = true; + tcllib_smoke.setName("tcllib pkg_smoke"); + tcllib_smoke.addFileArg(b.path("tools/pkg_smoke.tcl")); + tcllib_smoke.addArgs(&.{ "md5", "sha1", "cmdline" }); + tcllib_smoke.step.dependOn(&tcllib_install.step); + tcllib_smoke.step.dependOn(b.getInstallStep()); + tcllib_smoke.step.dependOn(install_libraries); + tcllib_step.dependOn(&tcllib_smoke.step); + + //-- tcllibc: tcllib's critcl accelerators, critcl driving zig cc (the + //tracked critcl_zig.config; zig resolved from PATH with our own dir + //prepended). critcl compiles against ITS OWN bundled headers, chosen by + //the version of the shell running it (critcl_c/tcl8.6 here) - which is + //why the generation split works with one config file. sak.tcl's critcl + //target expects a tclkitsh on windows, so this replicates its underlying + //invocation directly, as suite_tcl90 does. + const tcllibc_step = b.step("tcllibc", "build+install tcllibc (critcl accelerators, zig cc) under the suite-built 8.6 shell (+accel smoke/bench)"); + const critcl_run = b.addRunArtifact(tclsh_static_exe); + common.scrubTclEnv(critcl_run); + critcl_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src); + _ = critcl_run.captureStdOut(.{}); + critcl_run.setCwd(b.path("../tcllib")); + critcl_run.addFileArg(b.path("../critcl/main.tcl")); + critcl_run.addArg("-config"); + critcl_run.addFileArg(b.path("critcl_zig.config")); + //pin the build target to the config's explicit section: critcl otherwise + //GUESSES its target by probing PATH for compilers, and a scrubbed + //environment falls back to critcl's builtin msvc 'cl' target (G-102 + //finding, inherited here). + critcl_run.addArgs(&.{ "-target", "win32-x86_64-zig" }); + critcl_run.addArg("-cache"); + _ = critcl_run.addOutputDirectoryArg("critcl-cache"); + critcl_run.addArg("-force"); + critcl_run.addArg("-libdir"); + const critcl_libout = critcl_run.addOutputDirectoryArg("critcl-lib"); + critcl_run.addArgs(&.{ "-pkg", "tcllibc" }); + for (common.tcllibCritclFiles(b, "../tcllib")) |cf| { + critcl_run.addFileArg(b.path(b.pathJoin(&.{ "../tcllib/modules", cf }))); + } + const zigdir = std.fs.path.dirname(b.graph.zig_exe) orelse "."; + const inherited_path = b.graph.environ_map.get("PATH") orelse ""; + critcl_run.setEnvironmentVariable("PATH", b.fmt("{s}{c}{s}", .{ zigdir, std.fs.path.delimiter, inherited_path })); + const tcllibc_install = b.addInstallDirectory(.{ + .source_dir = critcl_libout, + .install_dir = .prefix, + .install_subdir = "lib", + }); + const tcllibc_smoke = b.addSystemCommand(&.{installed_static}); + common.scrubTclEnv(tcllibc_smoke); + tcllibc_smoke.has_side_effects = true; + tcllibc_smoke.setName("tcllibc accel smoke"); + tcllibc_smoke.addFileArg(b.path("tools/pkg_smoke.tcl")); + tcllibc_smoke.addArgs(&.{ "-accel", "md5", "tcllibc" }); + tcllibc_smoke.step.dependOn(&tcllibc_install.step); + tcllibc_smoke.step.dependOn(&tcllib_install.step); //accel host packages come from tcllib + tcllibc_smoke.step.dependOn(b.getInstallStep()); + tcllibc_smoke.step.dependOn(install_libraries); + //accelerated-vs-pure evidence (G-100 acceptance): same payload through + //sha1's critcl and pure-Tcl implementations, digests compared, ratio + //printed - the punkcheck-relevant hashing comparison. + const accel_bench = b.addSystemCommand(&.{installed_static}); + common.scrubTclEnv(accel_bench); + accel_bench.has_side_effects = true; + accel_bench.setName("tcllibc accel bench"); + accel_bench.addFileArg(b.path("tools/accel_bench.tcl")); + accel_bench.step.dependOn(&tcllibc_smoke.step); + tcllibc_step.dependOn(&accel_bench.step); + //-- test-gate: the 8.6 core testsuite under the installed static shell, //judged on PARSED totals (all.tcl exit codes lie) vs the tracked //dispositioned baseline. First census: run with -Dtestmode=record (the @@ -731,32 +888,57 @@ pub fn build(b: *std.Build) !void { gate_run.step.dependOn(install_libraries); gate_step.dependOn(&gate_run.step); - //-- per-library testsuites (suite_tcl90 G-107 pattern, thread+tclvfs - //arm): policy tiers gate|record|skip resolved at invocation. + //-- per-library testsuites (suite_tcl90 G-107 pattern): policy tiers + //gate|record|skip resolved at invocation. // -Dtestpolicy-=gate|record|skip -Dtestargs-="..." // -Dtestnotfiles-="a.test b.test" //Baselines expected_test_failures_thread.txt / _tclvfs.txt are created //from dispositioned record-mode censuses (same first-census flow as the - //core gate). + //core gate); the G-100 script-library suites ride the RECORD tier (their + //censuses are evidence, not a gate criterion - the same disposition + //suite_tcl90 made). + //NOTE zig runs independent steps concurrently - drive combined test + //invocations with -j1 (suite.tcl's test action does) so timing-sensitive + //suites never run beside heavy ones. const LibSuite = struct { - name: []const u8, - testsdir: []const u8, - driver: []const u8, + name: []const u8, //step suffix, policy/testargs option suffix, report basename + testsdir: []const u8, //run cwd (from build root) + driver: []const u8, //driver script, relative to testsdir default_policy: []const u8, - baseline: []const u8, - default_notfiles: []const u8, + baseline: []const u8, //tracked baseline filename ("" = none tracked yet) + default_notfiles: []const u8, //default driver -notfile exclusions ("" = none) + deps: []const *std.Build.Step, //beyond the top-level install step }; + const thread_test_deps = [_]*std.Build.Step{install_libraries}; + const tclvfs_test_deps = [_]*std.Build.Step{install_libraries}; + const tcllib_test_deps = [_]*std.Build.Step{ install_libraries, &tcllib_install.step, &tcllibc_install.step }; + const tklib_test_deps = [_]*std.Build.Step{ install_libraries, &tklib_install.step, &tcllib_install.step }; + const tk_test_deps = [_]*std.Build.Step{install_libraries}; const lib_suites = [_]LibSuite{ - .{ .name = "thread", .testsdir = "../tclthread/tests", .driver = "all.tcl", .default_policy = "gate", .baseline = "expected_test_failures_thread.txt", .default_notfiles = "" }, - .{ .name = "tclvfs", .testsdir = "../tclvfs/tests", .driver = "all.tcl", .default_policy = "gate", .baseline = "expected_test_failures_tclvfs.txt", .default_notfiles = "" }, + .{ .name = "thread", .testsdir = "../tclthread/tests", .driver = "all.tcl", .default_policy = "gate", .baseline = "expected_test_failures_thread.txt", .default_notfiles = "", .deps = &thread_test_deps }, + .{ .name = "tclvfs", .testsdir = "../tclvfs/tests", .driver = "all.tcl", .default_policy = "gate", .baseline = "expected_test_failures_tclvfs.txt", .default_notfiles = "", .deps = &tclvfs_test_deps }, + //tcllib via its own support/devel/all.tcl (sak's underlying driver). + //The installed tcllibc is on the shell's package path, so + //accelerator-aware suites engage the critcl accelerators - the + //highest-value run of the set for an 8.6 runtime nobody else builds. + .{ .name = "tcllib", .testsdir = "../tcllib", .driver = "support/devel/all.tcl", .default_policy = "record", .baseline = "", .default_notfiles = "", .deps = &tcllib_test_deps }, + .{ .name = "tklib", .testsdir = "../tklib", .driver = "support/devel/all.tcl", .default_policy = "record", .baseline = "", .default_notfiles = "", .deps = &tklib_test_deps }, + //tk: OPT-IN (not part of test-libraries) - it maps windows on the + //interactive desktop for tens of minutes. The native-dialog suites are + //excluded by default: their message-injection automation does not + //drive the real OS dialogs under a batch shell, so each case blocks + //waiting on a human. Re-include with -Dtestnotfiles-tk="" for a + //manual interactive run. + .{ .name = "tk", .testsdir = "../tk86/tests", .driver = "all.tcl", .default_policy = "record", .baseline = "", .default_notfiles = "winDialog.test winMsgbox.test", .deps = &tk_test_deps }, }; - const libtests_step = b.step("test-libraries", "library testsuites (thread, tclvfs) under the installed static shell per policy tier"); + const libtests_step = b.step("test-libraries", "library testsuites under the installed static shell per policy tier (default: thread+tclvfs gate, tcllib+tklib record; tk excluded - see test-tk)"); for (lib_suites) |ls| { const policy = b.option([]const u8, b.fmt("testpolicy-{s}", .{ls.name}), b.fmt("{s} testsuite policy: gate|record|skip (default {s})", .{ ls.name, ls.default_policy })) orelse ls.default_policy; const lib_testargs = b.option([]const u8, b.fmt("testargs-{s}", .{ls.name}), b.fmt("extra tcltest/driver args for the {s} testsuite", .{ls.name})) orelse ""; const lib_notfiles = b.option([]const u8, b.fmt("testnotfiles-{s}", .{ls.name}), b.fmt("test-file glob patterns excluded from the {s} run (default \"{s}\")", .{ ls.name, ls.default_notfiles })) orelse ls.default_notfiles; + const is_tk = std.mem.eql(u8, ls.name, "tk"); const step = b.step(b.fmt("test-{s}", .{ls.name}), b.fmt("{s} testsuite under the installed static shell (policy: {s})", .{ ls.name, policy })); - libtests_step.dependOn(step); + if (!is_tk) libtests_step.dependOn(step); if (std.mem.eql(u8, policy, "skip")) continue; if (!std.mem.eql(u8, policy, "gate") and !std.mem.eql(u8, policy, "record")) { std.debug.panic("-Dtestpolicy-{s}: unknown policy '{s}' (expected gate|record|skip)", .{ ls.name, policy }); @@ -772,10 +954,13 @@ pub fn build(b: *std.Build) !void { run.addArgs(&.{ "-driver", ls.driver }); if (std.mem.eql(u8, policy, "gate")) { run.addArg("-baseline"); - if (common.pathExists(b, ls.baseline)) { + if (ls.baseline.len != 0 and common.pathExists(b, ls.baseline)) { run.addFileArg(b.path(ls.baseline)); } else { - run.addArg(common.replaceAll(b, b.pathFromRoot(ls.baseline), "\\", "/")); + //no tracked baseline (yet): pass the conventional name so the + //engine's missing-baseline message names the file to create + const conv = if (ls.baseline.len != 0) ls.baseline else b.fmt("expected_test_failures_{s}.txt", .{ls.name}); + run.addArg(common.replaceAll(b, b.pathFromRoot(conv), "\\", "/")); } } run.addArg("-logfile"); @@ -820,6 +1005,11 @@ fn bootstrapMode(b: *std.Build) !void { .{ "tcl", "tcl86" }, .{ "tclthread", "tclthread" }, .{ "tclvfs", "tclvfs" }, + //G-100 dependency set + .{ "tk", "tk86" }, + .{ "tklib", "tklib" }, + .{ "tcllib", "tcllib" }, + .{ "critcl", "critcl" }, }; var deps_pending = false; for (source_dirs) |sd| { @@ -857,6 +1047,8 @@ fn bootstrapMode(b: *std.Build) !void { "build_zlib86", "build_tclthread86", "build_tclvfs86", + "build_tk86", + "critcl_zig.config", "expected_test_failures.txt", "expected_test_failures_thread.txt", "expected_test_failures_tclvfs.txt", @@ -874,7 +1066,8 @@ fn bootstrapMode(b: *std.Build) !void { //bootstrap: stage, then drive the staged recipe (per-zig-version cache dir - //object caches must never be shared across zig versions; forward-slash paths //throughout: the prefix flows into -D macros as C string literals) - const steps_opt = b.option([]const []const u8, "steps", "steps for the staged build (repeat the flag; default: install install-libraries smoke)") orelse @as([]const []const u8, &.{ "install", "install-libraries", "smoke" }); + //default mirrors suite.tcl's build default (keep the two in step) + const steps_opt = b.option([]const []const u8, "steps", "steps for the staged build (repeat the flag; default: install install-libraries smoke tklib tcllib tcllibc)") orelse @as([]const []const u8, &.{ "install", "install-libraries", "smoke", "tklib", "tcllib", "tcllibc" }); var cachever: []const u8 = builtin.zig_version_string; cachever = common.replaceAll(b, cachever, "+", "_"); cachever = common.replaceAll(b, cachever, "/", "_"); @@ -895,8 +1088,9 @@ fn bootstrapMode(b: *std.Build) !void { nested.has_side_effects = true; //cwd = staged build dir (suite.tcl parity) nested.setCwd(.{ .cwd_relative = b.pathFromRoot(b.pathJoin(&.{ stage_rel, "build" })) }); - //hermetic child shells (parity with suite.tcl) - for ([_][]const u8{ "TCLLIBPATH", "TCL_LIBRARY", "TK_LIBRARY" }) |ev| { + //hermetic child shells (parity with suite.tcl - same list as the per-step + //scrub, incl. the TCL__TM_PATH module roots) + for (common.tcl_env_vars) |ev| { nested.removeEnvironmentVariable(ev); } nested.step.dependOn(stage_step); diff --git a/src/buildsuites/suite_tcl86/build_common.zig b/src/buildsuites/suite_tcl86/build_common.zig index 00ffe985..e00e40b5 100644 --- a/src/buildsuites/suite_tcl86/build_common.zig +++ b/src/buildsuites/suite_tcl86/build_common.zig @@ -53,11 +53,28 @@ pub fn replaceAll(b: *std.Build, input: []const u8, needle: []const u8, replacem //never sets TCL_LIBRARY (built-shell runs execute the INSTALLED shell beside its //installed lib tree, proving hermetic exe-relative resolution). pub fn scrubTclEnv(run: *std.Build.Step.Run) void { - for ([_][]const u8{ "TCLLIBPATH", "TCL_LIBRARY", "TK_LIBRARY" }) |ev| { + for (tcl_env_vars) |ev| { run.removeEnvironmentVariable(ev); } } +//The full set of tcl-environment vars a suite child must not inherit. +//TCL__TM_PATH is the one that bites quietly (G-100 finding +//2026-07-26): tm.tcl seeds its module roots from env(TCL8_6_TM_PATH) down +//through every lower minor of the major, and this dev machine points +//TCL8_6_TM_PATH at C:/repo/jn/shellspy/modules among others - so a suite shell +//silently preferred a repo-built sha1-2.0.4.tm over the tcllib 2.0.5 the suite +//had just installed. Any census taken through such a shell measures the +//machine, not the suite. Scrubbed for every generation, not just this suite's: +//the vars are cheap to clear and a copied suite inherits the protection. +pub const tcl_env_vars = [_][]const u8{ + "TCLLIBPATH", "TCL_LIBRARY", "TK_LIBRARY", + "TCL8_0_TM_PATH", "TCL8_1_TM_PATH", "TCL8_2_TM_PATH", + "TCL8_3_TM_PATH", "TCL8_4_TM_PATH", "TCL8_5_TM_PATH", + "TCL8_6_TM_PATH", "TCL8_7_TM_PATH", "TCL9_0_TM_PATH", + "TCL9_1_TM_PATH", +}; + //checkout uuid from a tree's manifest.uuid when the repo materializes it (a //per-repo fossil 'manifest' setting; suite.tcl forces it on for staged //checkouts), else "unrecorded" so provenance emission stays total. @@ -122,6 +139,51 @@ pub fn pkgIfneededVersion(b: *std.Build, pkgindex_from_root: []const u8, pkgname std.debug.panic("no 'package ifneeded {s}' in {s}", .{ pkgname, pkgindex_from_root }); } +//tcllib-family installer trees carry 'package_version ' in +//support/installation/version.tcl (tklib and tcllib alike - both installers +//derive their default install dir name from it, and the suite passes it +//explicitly as -pkg-path so a machine-global tcl_pkgPath can never be chosen). +pub fn installerPackageVersion(b: *std.Build, tree_from_root: []const u8) []const u8 { + const data = readSourceFile(b, b.fmt("{s}/support/installation/version.tcl", .{tree_from_root})); + var lines = std.mem.splitScalar(u8, data, '\n'); + while (lines.next()) |line| { + var toks = std.mem.tokenizeAny(u8, line, " \t\r"); + const first = toks.next() orelse continue; + if (!std.mem.eql(u8, first, "package_version")) continue; + const ver = toks.next() orelse continue; + return b.dupe(ver); + } + std.debug.panic("no package_version in {s}/support/installation/version.tcl", .{tree_from_root}); +} + +//tcllib's critcl module files from its version.tcl declarations: the critcl_main +//module's files first, then every 'critcl' module's files (sak.tcl semantics). +//Returned paths are relative to the tcllib tree's modules/ dir. Tcl brace-list +//tokens are unwrapped by brace stripping (the declared file names contain no +//spaces). +pub fn tcllibCritclFiles(b: *std.Build, tcllib_from_root: []const u8) []const []const u8 { + const data = readSourceFile(b, b.fmt("{s}/support/installation/version.tcl", .{tcllib_from_root})); + var mains = std.array_list.Managed([]const u8).init(b.allocator); + var mods = std.array_list.Managed([]const u8).init(b.allocator); + var lines = std.mem.splitScalar(u8, data, '\n'); + while (lines.next()) |line| { + var toks = std.mem.tokenizeAny(u8, line, " \t\r"); + const first = toks.next() orelse continue; + const is_main = std.mem.eql(u8, first, "critcl_main"); + if (!is_main and !std.mem.eql(u8, first, "critcl")) continue; + _ = toks.next() orelse continue; //module name + while (toks.next()) |tok| { + var f = tok; + f = std.mem.trim(u8, f, "{}"); + if (f.len == 0) continue; + (if (is_main) &mains else &mods).append(b.dupe(f)) catch @panic("OOM"); + } + } + if (mains.items.len == 0) @panic("no critcl_main declaration found in tcllib version.tcl"); + mains.appendSlice(mods.items) catch @panic("OOM"); + return mains.items; +} + //TCL_WIN_VERSION as the makefiles derive it: //.., releaselevel //0=alpha 1=beta 2=final (e.g 8.6 + 8.6.18 -> 8.6.2.8618) diff --git a/src/buildsuites/suite_tcl86/build_tk86/build_tk86.zig b/src/buildsuites/suite_tcl86/build_tk86/build_tk86.zig new file mode 100644 index 00000000..8bd7feac --- /dev/null +++ b/src/buildsuites/suite_tcl86/build_tk86/build_tk86.zig @@ -0,0 +1,260 @@ +const std = @import("std"); +const common = @import("../build_common.zig"); + +//G-100: Tk 8.6 windows loadable extension (tk86t.dll) from stock core-8-6-branch +//sources, plus the tk script library installed to lib/tk8.6. +// +//DERIVED from the staged tk 8.6 win/makefile.vc + win/rules.vc - a re-derivation, +//not a port of the 9.0 build_tk.zig (the goal Context predicted this: the object +//lists and xlib arrangement differ). Differences from the 9.0 module, all +//makefile-authorised: +// - object inventory: 8.6 TKOBJS has no tkIcu / tkImgListFormat / tkImgSVGnano / +// tkPkgConfig, and no tkWinGDI / tkWinIco / tkWinSysTray. XLIBOBJS is the same +// 5-file emulation set but is a first-class makefile macro here (in 9.0 the +// xlib files had to be recovered by hand - the name-pattern extraction missed +// them). +// - NO tkPkgConfig.c in 8.6 -> no CFG_* configure-era defines at all (the 9.0 +// module carries a full CFG_{RUNTIME,INSTALL}_* set for it). +// - dll naming: rules.vc's TCL_MAJOR_VERSION==8 arm gives TKLIBNAME +// tk86t.dll (SUFX 't' survives while TCL_VERSION <= 86); the 9.0 arm produces +// the tcl9tk90.dll form. +// - wish.exe.manifest version: 8.6's makefile substitutes @TK_WIN_VERSION@ with +// $(DOTVERSION).0.0 = "8.6.0.0" - NOT the tcl-style +// .. formula the 9.0 tree uses. +// - pkgIndex: the 8.6 install-binaries text provides ONLY 'Tk' (no lowercase +// 'tk' alias) and guards with 'package vsatisfies [package provide Tcl] 8.6.0'. +// +//Kept from the 9.0 module (same reasons): +// - USE_TCL_STUBS/USE_TCLOO_STUBS + link against the suite's libtclstub86.a: +// that IS the stock arrangement (rules.vc sets USE_STUBS_DEFS for every +// project except tcl itself), and it keeps the dll unbound to a particular +// tcl dll name so both the static and dynamic suite shells can load it. +// - tkMain.c compiled twice (makefile: tkMain.obj + tkMain2.obj). The ansi copy +// gets explicit -UUNICODE/-U_UNICODE so a driver-side UNICODE default could +// never turn it into a duplicate UNICODE compile (G-096 finding; verified +// 2026-07-26 that the pinned zig cc defines neither for x86_64-windows-gnu, +// so the guard is belt-and-braces and the stock ANSI/TCHAR arrangement of the +// 8.6 core is matched exactly). +// - tkUuid.h generated into the build cache (tkWindow.c #includes it) and +// consumed via an include-dir overlay added FIRST - source trees are never +// written (G-102 convention). + +//win/makefile.vc TKOBJS, split by the directory each source actually lives in +//(verified against the staged tree - every name resolved to exactly one dir). +const generic_names = [_][]const u8{ + "tkConsole", "tk3d", "tkArgv", "tkAtom", "tkBind", + "tkBitmap", "tkBusy", "tkButton", "tkCanvArc", "tkCanvBmap", + "tkCanvImg", "tkCanvLine", "tkCanvPoly", "tkCanvPs", "tkCanvText", + "tkCanvUtil", "tkCanvWind", "tkCanvas", "tkClipboard", "tkCmds", + "tkColor", "tkConfig", "tkCursor", "tkEntry", "tkError", + "tkEvent", "tkFileFilter", "tkFocus", "tkFont", "tkFrame", + "tkGC", "tkGeometry", "tkGet", "tkGrab", "tkGrid", + "tkImage", "tkImgBmap", "tkImgGIF", "tkImgPNG", "tkImgPPM", + "tkImgPhoto", "tkImgPhInstance", "tkImgUtil", "tkListbox", "tkMacWinMenu", + "tkMenu", "tkMenubutton", "tkMenuDraw", "tkMessage", "tkPanedWindow", + "tkObj", "tkOldConfig", "tkOption", "tkPack", "tkPlace", + "tkPointer", "tkRectOval", "tkScale", "tkScrollbar", "tkSelect", + "tkStyle", "tkText", "tkTextBTree", "tkTextDisp", "tkTextImage", + "tkTextIndex", "tkTextMark", "tkTextTag", "tkTextWind", "tkTrig", + "tkUndo", "tkUtil", "tkVisual", "tkStubInit", "tkWindow", +}; +const unix_names = [_][]const u8{ "tkUnixMenubu", "tkUnixScale" }; +const xlib_names = [_][]const u8{ "xcolors", "xdraw", "xgc", "ximage", "xutil" }; +const win_names = [_][]const u8{ + "tkWin3d", "tkWin32Dll", "tkWinButton", "tkWinClipboard", "tkWinColor", + "tkWinConfig", "tkWinCursor", "tkWinDialog", "tkWinDraw", "tkWinEmbed", + "tkWinFont", "tkWinImage", "tkWinInit", "tkWinKey", "tkWinMenu", + "tkWinPixmap", "tkWinPointer", "tkWinRegion", "tkWinScrlbr", "tkWinSend", + "tkWinSendCom", "tkWinWindow", "tkWinWm", "tkWinX", "stubs", + "ttkWinMonitor", "ttkWinTheme", "ttkWinXPTheme", +}; +const ttk_names = [_][]const u8{ + "ttkBlink", "ttkButton", "ttkCache", "ttkClamTheme", "ttkClassicTheme", + "ttkDefaultTheme", "ttkElements", "ttkEntry", "ttkFrame", "ttkImage", + "ttkInit", "ttkLabel", "ttkLayout", "ttkManager", "ttkNotebook", + "ttkPanedwindow", "ttkProgress", "ttkScale", "ttkScrollbar", "ttkScroll", + "ttkSeparator", "ttkSquare", "ttkState", "ttkTagSet", "ttkTheme", + "ttkTrace", "ttkTrack", "ttkTreeview", "ttkWidget", "ttkStubInit", +}; + +pub const TkBuild = struct { + lib: *std.Build.Step.Compile, + pkgidx: std.Build.LazyPath, + patchlevel: []const u8, //e.g "8.6.17" (tk.h TK_PATCH_LEVEL) + libdir: []const u8, //install dir name, e.g "tk8.6" + dll_file: []const u8, //e.g "tk86t.dll" +}; + +pub fn build_tk86(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, stublib: *std.Build.Step.Compile) !TkBuild { + //-- version facts from the staged tree (never hardcoded) + const tkh_content = common.readSourceFile(b, subdir ++ "/generic/tk.h"); + const tk_h_version = common.parseDefineString(tkh_content, "TK_VERSION") orelse @panic("TK_VERSION not found in tk.h"); + const tk_h_patchlevel = common.parseDefineString(tkh_content, "TK_PATCH_LEVEL") orelse @panic("TK_PATCH_LEVEL not found in tk.h"); + if (!std.mem.eql(u8, tk_h_version, "8.6")) { + std.debug.panic("staged tk tree is {s} but this recipe expresses Tk 8.6 (sources.config tk ref / dir mismatch?)", .{tk_h_version}); + } + const tk_nodot = "86"; + const dll_name = "tk" ++ tk_nodot ++ "t"; //rules.vc TKLIBNAME, TCL_MAJOR_VERSION==8 arm + const dll_file = dll_name ++ ".dll"; + const script_dir_rel = "lib/tk8.6"; //rules.vc SCRIPT_INSTALL_DIR for DOING_TK + + const lib = b.addLibrary(.{ + .linkage = .dynamic, + .name = dll_name, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), + }); + + //-- flag classes (rules.vc pkgcflags for PROJECT=tk: cflags + INCLUDES + + //TCL_DEFINES + PRJ_DEFINES + OPTDEFINES + USE_STUBS_DEFS + /DBUILD_tk) + var flags = std.array_list.Managed([]const u8).init(b.allocator); + defer flags.deinit(); + try flags.appendSlice(&.{ + "-Wall", + "-O3", + "-fomit-frame-pointer", + "-finput-charset=UTF-8", + //the tcl 8.6 recipe's finding, kept here: clang UBSan traps must stay + //fully off on windows + "-fno-sanitize=undefined", + "-fno-sanitize-trap=undefined", + //project defines: makefile.vc PRJ_DEFINES (/DBUILD_ttk + CONFIG_DEFS; + //inline=__inline and the _CRT_*_NO_DEPRECATE pair are MSVC-isms) + "-DBUILD_tk", + "-DBUILD_ttk", + "-DSTDC_HEADERS=1", + "-DHAVE_SYS_TYPES_H=1", + "-DHAVE_SYS_STAT_H=1", + "-DHAVE_STDLIB_H=1", + "-DHAVE_STRING_H=1", + "-DHAVE_MEMORY_H=1", + "-DHAVE_STRINGS_H=1", + "-DHAVE_UXTHEME_H=1", //makefile default (OPTS=noxp turns it off) + //rules.vc USE_STUBS_DEFS for a non-tcl project + "-DUSE_TCL_STUBS=1", + "-DUSE_TCLOO_STUBS=1", + //OPTDEFINES equivalents, matching the flag set the 8.6 core was built with + "-DTCL_THREADS=1", + "-DNDEBUG=1", + "-DTCL_CFG_OPTIMIZED=1", + "-DTCL_CFG_DO64BIT=1", + "-DHAVE_NO_SEH=1", //gcc-class: no __try/__except + }); + + var sources = std.array_list.Managed([]const u8).init(b.allocator); + inline for (generic_names) |n| try sources.append(subdir ++ "/generic/" ++ n ++ ".c"); + inline for (unix_names) |n| try sources.append(subdir ++ "/unix/" ++ n ++ ".c"); + inline for (xlib_names) |n| try sources.append(subdir ++ "/xlib/" ++ n ++ ".c"); + inline for (win_names) |n| try sources.append(subdir ++ "/win/" ++ n ++ ".c"); + inline for (ttk_names) |n| try sources.append(subdir ++ "/generic/ttk/" ++ n ++ ".c"); + + //tkUuid.h overlay: makefile.vc concatenates win/tkUuid.h.in + the checkout's + //manifest.uuid; wrapfiletofile reproduces exactly that. + const wrapfilecmd = b.addExecutable(.{ + .name = "wrapfiletofile", + .root_module = b.createModule(.{ + .root_source_file = b.path("tools/wrapfiletofile.zig"), + .target = b.graph.host, + }), + }); + const uuidcmd_step = b.addRunArtifact(wrapfilecmd); + uuidcmd_step.addArgs(&.{ + "-prefix", + "#define TK_VERSION_UUID \\", + "-prefixnl", + "1", + "-input", + }); + uuidcmd_step.addFileArg(b.path(subdir ++ "/manifest.uuid")); + uuidcmd_step.addArg("-output"); + const tkuuid_file = uuidcmd_step.addOutputFileArg("tkUuid.h"); + lib.root_module.addIncludePath(tkuuid_file.dirname()); //overlay first + + lib.root_module.addIncludePath(b.path(subdir ++ "/generic")); + lib.root_module.addIncludePath(b.path(subdir ++ "/generic/ttk")); + lib.root_module.addIncludePath(b.path(subdir ++ "/win")); + lib.root_module.addIncludePath(b.path(subdir ++ "/xlib")); //PRJ_INCLUDES + lib.root_module.addIncludePath(b.path(subdir ++ "/bitmaps")); //PRJ_INCLUDES + lib.root_module.addIncludePath(b.path(tcldir ++ "/generic")); + lib.root_module.addIncludePath(b.path(tcldir ++ "/win")); + + lib.root_module.addCSourceFiles(.{ + .files = sources.items, + .flags = flags.items, + }); + + //tkMain.c twice - ansi (forced) + UNICODE 'tkMain2' (makefile.vc parity) + var mainansi_flags = std.array_list.Managed([]const u8).init(b.allocator); + defer mainansi_flags.deinit(); + try mainansi_flags.appendSlice(flags.items); + try mainansi_flags.appendSlice(&.{ "-UUNICODE", "-U_UNICODE" }); + lib.root_module.addCSourceFile(.{ + .file = b.path(subdir ++ "/generic/tkMain.c"), + .flags = mainansi_flags.items, + }); + var mainw_flags = std.array_list.Managed([]const u8).init(b.allocator); + defer mainw_flags.deinit(); + try mainw_flags.appendSlice(flags.items); + try mainw_flags.appendSlice(&.{ "-DUNICODE", "-D_UNICODE" }); + lib.root_module.addCSourceFile(.{ + .file = b.path(subdir ++ "/generic/tkMain.c"), + .flags = mainw_flags.items, + }); + + //wish.exe.manifest overlay: win/rc/tk.rc embeds it as RT_MANIFEST (that is + //what enables comctl32 v6 themed scrollbars in the DLL), and RT_MANIFEST + //file references resolve through the rc include paths. 8.6's makefile + //substitutes @TK_WIN_VERSION@ with $(DOTVERSION).0.0. + const tk_win_version = b.fmt("{s}.0.0", .{tk_h_version}); + const wish_manifest_content = common.replaceAll(b, common.replaceAll(b, common.readSourceFile(b, subdir ++ "/win/wish.exe.manifest.in"), "@TK_WIN_VERSION@", tk_win_version), "@MACHINE@", "AMD64"); + const wf_tkrc = b.addWriteFiles(); + _ = wf_tkrc.add("wish.exe.manifest", wish_manifest_content); + lib.root_module.addWin32ResourceFile(.{ + .file = b.path(subdir ++ "/win/rc/tk.rc"), + //TCL_THREADS drives the rc's OriginalFilename suffix (tk86t.dll) + .flags = &.{"-DTCL_THREADS=1"}, + .include_paths = &.{ + wf_tkrc.getDirectory(), + b.path(subdir ++ "/win"), + b.path(subdir ++ "/win/rc"), + b.path(subdir ++ "/generic"), + b.path(tcldir ++ "/generic"), + }, + }); + + lib.root_module.linkLibrary(stublib); + lib.root_module.link_libc = true; + //rules.vc winlibs + makefile.vc PRJ_LIBS, plus the OLE/dialog/IME imports the + //win sources pull in (tkWinSendCom, tkWinDialog, tkWinX) + inline for (.{ "netapi32", "gdi32", "user32", "userenv", "winspool", "shell32", "ole32", "oleaut32", "uuid", "comctl32", "comdlg32", "imm32", "uxtheme", "advapi32", "kernel32" }) |l| { + lib.root_module.linkSystemLibrary(l, .{}); + } + + b.installArtifact(lib); //bin/tk86t.dll (+ tk86t.lib import lib -> lib/) + + //tk script library -> /lib/tk8.6 (whole-tree copy: stock splits the + //demos out to a separate dir, this keeps them inside - same simplification + //the tcl-side library install makes) + const install_tk_library = b.addInstallDirectory(.{ + .source_dir = b.path(subdir ++ "/library"), + .install_dir = .prefix, + .install_subdir = script_dir_rel, + }); + b.getInstallStep().dependOn(&install_tk_library.step); + + //pkgIndex.tcl per makefile.vc install-binaries (windows arm of its two-branch + //text - the unix DISPLAY branch cannot apply to a windows-only suite; + //'file normalize' added as in the 9.0 recipe so the load path is cwd-proof). + const wf_pkgidx = b.addWriteFiles(); + const pkgidx = wf_pkgidx.add("pkgIndex.tcl", b.fmt( + \\if {{![package vsatisfies [package provide Tcl] 8.6.0]}} return + \\package ifneeded Tk {s} [list load [file normalize [file join $dir .. .. bin {s}]]] + \\ + , .{ tk_h_patchlevel, dll_file })); + const pkgidx_install = b.addInstallFileWithDir(pkgidx, .prefix, script_dir_rel ++ "/pkgIndex.tcl"); + b.getInstallStep().dependOn(&pkgidx_install.step); + + return .{ .lib = lib, .pkgidx = pkgidx, .patchlevel = tk_h_patchlevel, .libdir = "tk8.6", .dll_file = dll_file }; +} diff --git a/src/buildsuites/suite_tcl86/critcl_zig.config b/src/buildsuites/suite_tcl86/critcl_zig.config new file mode 100644 index 00000000..74a0a7ed --- /dev/null +++ b/src/buildsuites/suite_tcl86/critcl_zig.config @@ -0,0 +1,36 @@ +# suite_tcl86 critcl configuration: zig cc as the C toolchain (G-100). +# Forked from suite_tcl90/critcl_zig.config - generation-neutral content: critcl +# compiles against whichever tcl headers/stubs the DRIVING shell reports, so the +# 8.6 suite needs no flag differences here. 'zig' is resolved from PATH - the +# suite recipe prepends the pinned bin/tools zig dir for the critcl invocation. +# Target is the native windows-gnu triple (matches the suite's tcl runtime ABI). +# +# The settings live under an EXPLICIT target, win32-x86_64-zig, and the recipe +# invokes critcl with '-target win32-x86_64-zig': critcl otherwise chooses its +# build target by PROBING the environment for compilers, which made the selection +# depend on what happened to be on PATH (a visible gcc landed on the config +# defaults; a scrubbed environment fell back to critcl's builtin msvc 'cl' +# target and never used these settings - suite_tcl90 G-102 finding). + +platform win32-x86_64 + +win32-x86_64-zig platform win32-x86_64 +win32-x86_64-zig compile zig cc -target x86_64-windows-gnu -c -fPIC +win32-x86_64-zig version zig version +win32-x86_64-zig link zig cc -target x86_64-windows-gnu -shared +win32-x86_64-zig include -I +win32-x86_64-zig libinclude -L +win32-x86_64-zig object .o +win32-x86_64-zig output -o [list $outfile] +win32-x86_64-zig optimize -O2 +win32-x86_64-zig noassert -DNDEBUG +win32-x86_64-zig tclstubs -DUSE_TCL_STUBS +win32-x86_64-zig tkstubs -DUSE_TK_STUBS +win32-x86_64-zig debug_symbols -g +win32-x86_64-zig strip -Wl,-s +win32-x86_64-zig link_debug +win32-x86_64-zig link_release +win32-x86_64-zig embed_manifest +win32-x86_64-zig ldoutput +win32-x86_64-zig link_preload +win32-x86_64-zig sharedlibext .dll diff --git a/src/buildsuites/suite_tcl86/expected_test_failures_tclvfs.txt b/src/buildsuites/suite_tcl86/expected_test_failures_tclvfs.txt index 1e834aa9..09c66533 100644 --- a/src/buildsuites/suite_tcl86/expected_test_failures_tclvfs.txt +++ b/src/buildsuites/suite_tcl86/expected_test_failures_tclvfs.txt @@ -15,12 +15,25 @@ # against the retired external FTP host - the same 1.4.2-era entries that # suite_tcl90's tclvfs baseline note records from its 2026-07-21 capture. -# -- external-service dependency: both tests reach a public FTP server +# -- external-service dependency: these tests reach a public FTP server # (ftp://ftp.tcl.tk) the Tcl project retired years ago, so they fail on any # modern machine/network regardless of the build (vfsUrl-1.2 asserts -# 'file exists ftp://ftp.tcl.tk' == 1; vfsUrl-2.1 copies a file from it). Not a -# suite defect - the tclvfs URL handlers themselves are exercised by the passing -# vfsUrl mount/parse cases. (The file's other ftp cases are gated behind the -# 'ftp' constraint and skip; these two are not constraint-gated.) +# 'file exists ftp://ftp.tcl.tk' == 1; vfsUrl-2.1 copies a file from it; +# vfsFtp-1.1..1.3 mount/list/stat that host). Not a suite defect - the tclvfs +# URL/FTP handlers themselves are exercised by the passing mount/parse cases. +# +# G-100 EXTENSION 2026-07-26: the vfsFtp entries joined the baseline when the +# suite grew tcllib. They are gated behind the 'ftp' constraint, which needs +# tcllib's ftp package - absent at the G-099 capture (3 constraint skips), +# satisfied once 'suite.tcl build' installs tcllib 2.0, so they now RUN and +# reach the retired host. Skips accordingly dropped 8 -> 5. +# +# WHICH members of this family fail is network-timing dependent (a connect that +# times out vs one refused fast can flip a case between fail and error-then-fail +# across runs), so the gate is expected to report some of these as 'stale +# baseline' entries on any given run - stale entries are noted, never fatal. vfsUrl-1.2 vfsUrl-2.1 +vfsFtp-1.1 +vfsFtp-1.2 +vfsFtp-1.3 diff --git a/src/buildsuites/suite_tcl86/sources.config b/src/buildsuites/suite_tcl86/sources.config index 8d550191..59d805a4 100644 --- a/src/buildsuites/suite_tcl86/sources.config +++ b/src/buildsuites/suite_tcl86/sources.config @@ -27,14 +27,34 @@ # (2.8.13, 2026-04-30) - selection recorded in goals G-099. # - tclvfs keeps TRUNK (1.4.2 era): the forTcl9 branch (1.5.0) is for tcl9 # suites only (user decision 2026-07-22, recorded in suite_tcl90). +# +# G-100 dependency-set notes: +# - tk rides core-8-6-branch (the 8.6 maintenance line, pairing with the 8.6 +# runtime) into dir tk86 - the 9.0 recipe's tk9 dir is version-named the +# same way. The object lists/xlib arrangement differ from 9.0, so +# build_tk86 is a re-derivation from the 8.6 win/makefile.vc, not a reuse. +# - tklib/tcllib/critcl are GENERATION-NEUTRAL upstreams: the same trunk refs +# (and the same zon pins) as suite_tcl90 - what differs is the shell that +# runs their installers and the critcl target that builds tcllibc. Both +# libraries install per-generation (tcl8 vs tcl9 tcllibc side by side is the +# established punk kit payload shape). -description Zig-built Tcl 8.6 windows runtime (core-8-6-branch): tclsh86ts/tclsh86t shells + tcl86t.dll + installed lib tree, thread 2.8 + tclvfs, core-test gate (G-099 arc) +description Zig-built Tcl 8.6 windows runtime (core-8-6-branch): tclsh86ts/tclsh86t shells + tcl86t.dll + installed lib tree, thread 2.8 + tclvfs + Tk 8.6 + tklib/tcllib(+tcllibc), core-test gate (G-099/G-100 arc) product tclsh86ts.exe static shell + tclsh86t.exe/tcl86t.dll dynamic pair with installed lib/tcl8.6 tree + tm modules (no zipfs in 8.6 - no self-contained szip runtime; kit containment is G-101 territory) product thread 2.8.13 (thread2813t.dll) and tclvfs 1.4.2 (vfs142t.dll) + registry/dde package dlls, all loadable in both shells -product core + thread + tclvfs testsuite gates vs tracked expected_test_failures baselines (parsed all.tcl totals; first censuses in flight - see README) +product Tk 8.6.17 (tk86t.dll + lib/tk8.6 script library) with tklib 0.9 and tcllib 2.0 installed by their own installers, plus tcllibc critcl accelerators built with zig cc +product core + thread + tclvfs testsuite gates vs tracked expected_test_failures baselines (parsed all.tcl totals), tcllib/tklib record tiers and an opt-in test-tk doc README.md zigpin bin/tools/zig-x86_64-windows-0.16.0/zig.exe source tcl fossil https://core.tcl-lang.org/tcl core-8-6-branch tcl86 source tclthread fossil https://core.tcl-lang.org/thread thread-2-8-branch tclthread source tclvfs fossil https://core.tcl-lang.org/tclvfs trunk tclvfs + +# G-100: Tk + companion script libraries. tklib is pure-tcl (installed by its own +# installer where that matters); tcllib's accelerators (tcllibc) are built via critcl. +source tk fossil https://core.tcl-lang.org/tk core-8-6-branch tk86 +source tklib fossil https://core.tcl-lang.org/tklib trunk tklib +source tcllib fossil https://core.tcl-lang.org/tcllib trunk tcllib + +source critcl git https://github.com/andreas-kupries/critcl 3.3.1 critcl diff --git a/src/buildsuites/suite_tcl86/suite.tcl b/src/buildsuites/suite_tcl86/suite.tcl index ba3cacd3..ad001bb7 100644 --- a/src/buildsuites/suite_tcl86/suite.tcl +++ b/src/buildsuites/suite_tcl86/suite.tcl @@ -24,11 +24,16 @@ # -seedfossils read-only seed collection for missing clones (default ~/.fossils; "" disables) # -all <0|1> with 'clean': reserved (the clone store lives outside the stage) # -steps zig build steps. 'build' default: {install install-libraries -# smoke} (runtimes + companions + script library + smokes). +# smoke tklib tcllib tcllibc} (runtimes + companions + Tk + +# script library + smokes, then the G-100 dependency set: +# tklib/tcllib installed by their own installers and the +# critcl-built tcllibc accelerators). # 'test' default: {test-gate} (the 8.6 core suite gate vs the # tracked expected_test_failures baseline; first census via # -zigargs {-Dtestmode=record}). Also: test-thread, test-tclvfs, -# test-libraries (G-107-pattern policy tiers). +# test-libraries (G-107-pattern policy tiers), test-tcllib, +# test-tklib, and the OPT-IN test-tk (full Tk suite: maps +# windows on the interactive desktop, tens of minutes). # -zigargs extra args appended to the zig build invocation # #The build area lives entirely under src/buildsuites/_build (VCS-ignored). Fossil @@ -119,7 +124,7 @@ array set opt { -optimize ReleaseFast -tclbranch {} -refresh 0 - -steps {install install-libraries smoke} + -steps {install install-libraries smoke tklib tcllib tcllibc} -testargs {} -zigargs {} -repofolder {} @@ -170,9 +175,15 @@ if {$opt(-repofolder) ne ""} { #Hermetic child shells: the suite-built tclsh must not see the user's machine-level #package paths (TCLLIBPATH/TCL_LIBRARY misdirect installer paths and risk -#false-positive smokes). The zig recipe additionally scrubs these per built-shell -#Run step (suite_tcl90 G-102 convention, kept for the 8.6 recipe). -foreach ev {TCLLIBPATH TCL_LIBRARY TK_LIBRARY} { +#false-positive smokes; TCL__TM_PATH seeds tm.tcl's module roots and +#is the quiet one - this machine points TCL8_6_TM_PATH at repo/foreign module trees, +#which shadowed suite-installed tcllib packages until the G-100 work caught it). +#The zig recipe scrubs the same list per built-shell Run step (build_common.zig +#tcl_env_vars - keep the two lists in step). +foreach ev {TCLLIBPATH TCL_LIBRARY TK_LIBRARY + TCL8_0_TM_PATH TCL8_1_TM_PATH TCL8_2_TM_PATH TCL8_3_TM_PATH + TCL8_4_TM_PATH TCL8_5_TM_PATH TCL8_6_TM_PATH TCL8_7_TM_PATH + TCL9_0_TM_PATH TCL9_1_TM_PATH} { if {[info exists env($ev)]} { log "unsetting inherited \$env($ev) for hermetic child shells" unset env($ev) @@ -276,19 +287,33 @@ proc fossil_source {name url branch dir} { cd $savedpwd } +proc git_source {name url ref dir} { + #clone (or leave) a git checkout pinned at ref (critcl - the recipe's tcllibc + #step consumes it from the stage; suite_tcl90 convention) + global opt + if {![file isdirectory [file join $dir .git]]} { + log "cloning $name (git) -> $dir" + run git clone --quiet $url $dir + } elseif {$opt(-refresh)} { + run git -C $dir fetch --quiet + } + run git -C $dir checkout --quiet $ref +} + #-- stage the recipe ------------------------------------------------------ #Per-item sync (not a wholesale delete of the build dir): zig cache dirs live in #the build dir and are kept (per-zig-version; windows can hold transient locks on -#recently-used cache trees). +#recently-used cache trees). Item list mirrors the zig bootstrap mode's +#recipe_items (build86.zig bootstrapMode). log "staging recipe -> $builddir" file mkdir $builddir -foreach item {build86.zig build_common.zig build_zlib86 build_tclthread86 build_tclvfs86 expected_test_failures.txt expected_test_failures_thread.txt expected_test_failures_tclvfs.txt src tools} { +foreach item {build86.zig build_common.zig build_zlib86 build_tclthread86 build_tclvfs86 build_tk86 critcl_zig.config expected_test_failures.txt expected_test_failures_thread.txt expected_test_failures_tclvfs.txt src tools} { file delete -force [file join $builddir $item] file copy [file join $suiteroot $item] $builddir } #-- sources --------------------------------------------------------------- -foreach required {tcl tclthread tclvfs} { +foreach required {tcl tclthread tclvfs tk tklib tcllib critcl} { if {![dict exists $sources $required]} { fail "sources.config: missing required source record '$required'" } @@ -301,6 +326,7 @@ dict for {name s} $sources { } switch -- [dict get $s kind] { fossil {fossil_source $name [dict get $s url] $ref [file join $stage [dict get $s dir]]} + git {git_source $name [dict get $s url] $ref [file join $stage [dict get $s dir]]} } } diff --git a/src/buildsuites/suite_tcl86/tools/accel_bench.tcl b/src/buildsuites/suite_tcl86/tools/accel_bench.tcl new file mode 100644 index 00000000..2882937e --- /dev/null +++ b/src/buildsuites/suite_tcl86/tools/accel_bench.tcl @@ -0,0 +1,50 @@ +#accel_bench.tcl (G-100): tcllibc accelerator evidence under a suite-built shell. +#Hashes the same payload twice through tcllib's sha1 - once on the critcl +#accelerator (tcllibc), once on the pure-Tcl implementation - asserts the two +#digests match and prints the timings plus the speedup ratio. This is the +#punkcheck-relevant comparison (punkcheck hashing is the workload that made the +#accelerators worth building). +# +#Both implementations are made available with ::sha1::LoadAccelerator (the +#package itself stops loading at the FIRST working accelerator, so 'tcl' is +#normally not registered when critcl won) and selected with ::sha1::SwitchTo. +# +#args: ?-mb ? (default 4) + +proc fail {msg} {puts stderr "accel_bench FAIL: $msg"; flush stderr; exit 1} + +set mb 4 +for {set i 0} {$i < [llength $argv]} {incr i} { + switch -- [lindex $argv $i] { + -mb {incr i; set mb [lindex $argv $i]} + default {fail "unknown argument: [lindex $argv $i]"} + } +} + +if {[catch {package require sha1} sha1ver]} {fail "package require sha1: $sha1ver"} +if {[catch {package require tcllibc} tcllibcver]} {fail "package require tcllibc: $tcllibcver"} + +if {![::sha1::LoadAccelerator critcl]} {fail "sha1 critcl accelerator not available (tcllibc $tcllibcver loaded but sha1c missing?)"} +if {![::sha1::LoadAccelerator tcl]} {fail "sha1 pure-tcl implementation not available"} + +#payload: incompressible-ish repeated block, built once, hashed by both +set block [string repeat "punkshell-suite_tcl86-accel-bench-0123456789abcdef" 1024] +set data "" +set target [expr {$mb * 1024 * 1024}] +while {[string length $data] < $target} {append data $block} +set bytes [string length $data] + +::sha1::SwitchTo critcl +set t_critcl [lindex [time {set d_critcl [::sha1::sha1 -hex -- $data]}] 0] +::sha1::SwitchTo tcl +set t_tcl [lindex [time {set d_tcl [::sha1::sha1 -hex -- $data]}] 0] + +if {$d_critcl ne $d_tcl} {fail "digest mismatch: critcl $d_critcl vs pure-tcl $d_tcl"} +if {$t_critcl <= 0} {set t_critcl 1} + +set speedup [format %.1f [expr {double($t_tcl) / double($t_critcl)}]] +set ms_critcl [format %.1f [expr {$t_critcl / 1000.0}]] +set ms_tcl [format %.1f [expr {$t_tcl / 1000.0}]] +puts "accel_bench OK: sha1 over $bytes bytes - critcl ${ms_critcl}ms vs pure-tcl ${ms_tcl}ms = ${speedup}x (digests identical, sha1 $sha1ver, tcllibc $tcllibcver)" +flush stdout +exit 0 diff --git a/src/buildsuites/suite_tcl86/tools/tk_smoke.tcl b/src/buildsuites/suite_tcl86/tools/tk_smoke.tcl new file mode 100644 index 00000000..a1168ff3 --- /dev/null +++ b/src/buildsuites/suite_tcl86/tools/tk_smoke.tcl @@ -0,0 +1,64 @@ +#tk_smoke.tcl (G-100): Tk window-station smoke under a suite-built shell. +#Goes beyond 'package require Tk' (a load-only check passes even when no usable +#display exists): it maps a real toplevel with a widget in it, forces a display +#update, then destroys it - the create/destroy-window verification the G-100 +#acceptance asks for, run on every build rather than once by hand. +# +#args: ?-expect ? +#Exits 1 with a 'tk_smoke FAIL:' line on any failure; prints one evidence line +#on success (version, screen geometry/depth, windowingsystem, widget classes). + +proc fail {msg} {puts stderr "tk_smoke FAIL: $msg"; flush stderr; exit 1} + +set expect {} +for {set i 0} {$i < [llength $argv]} {incr i} { + switch -- [lindex $argv $i] { + -expect {incr i; set expect [lindex $argv $i]} + default {fail "unknown argument: [lindex $argv $i]"} + } +} + +if {[catch {package require Tk} tkver]} {fail "package require Tk: $tkver"} +if {$expect ne "" && $tkver ne $expect} {fail "Tk version $tkver, expected $expect"} +if {[catch {package require Ttk} ttkver]} {set ttkver "-"} + +#the root window itself: geometry facts prove a real window station/display +if {[catch { + wm title . "tk_smoke" + wm geometry . 320x120+80+80 + set screen "[winfo screenwidth .]x[winfo screenheight .] depth [winfo screendepth .]" + set wsys [tk windowingsystem] +} err]} {fail "root window setup: $err"} + +#create: a classic widget, a themed widget and a child toplevel +if {[catch { + label .l -text "tk_smoke" + pack .l + ttk::button .b -text "ok" + pack .b + toplevel .t + wm title .t "tk_smoke child" + wm geometry .t 200x80+420+80 + label .t.l -text "child" + pack .t.l + update +} err]} {fail "widget create/map: $err"} + +foreach {label w} {root . classic .l themed .b child .t} { + if {![winfo ismapped $w]} {fail "window not mapped after update: $label ($w)"} +} +set classes [list [winfo class .l] [winfo class .b] [winfo class .t]] + +#destroy: the child toplevel first, then every root child - a clean teardown +if {[catch { + destroy .t + update + if {[winfo exists .t]} {fail "child toplevel survived destroy"} + destroy .l .b + update + if {[llength [winfo children .]]} {fail "root children survived destroy: [winfo children .]"} +} err]} {fail "widget destroy: $err"} + +puts "tk_smoke OK: Tk $tkver (Ttk $ttkver) windowingsystem $wsys screen $screen widgets [join $classes /] create+map+destroy clean" +flush stdout +exit 0