Browse Source

G-098 increment 3: tcllib installed; tcllibc via critcl 3.3.1 + zig cc; sha1 248x evidence

tcllib pure side via its own installer (explicit -pkg-path; driver now unsets
TCLLIBPATH/TCL_LIBRARY/TK_LIBRARY for hermetic child shells after the default
libdir followed inherited TCLLIBPATH to C:/TCLPKGS - additive pollution
verified and removed). install-libraries now installs the whole tcl library
tree (its own pkgIndex references msgcat/opt/... subdirs the piecemeal
install missed). critcl pinned 3.3.1 (git) + tracked critcl_zig.config
(zig cc -target x86_64-windows-gnu); driver replicates sak's direct critcl
invocation over the 21 declared files -> tcllibc.dll at out/lib/tcllibc
(provide 2.0). Evidence: md5 accel(critcl)=1; sha1 5MB critcl 12.3ms vs
pure 3.06s (~248x) - the punkcheck hashing pain quantified and fixed.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 2 weeks ago
parent
commit
48a1d0a97f
  1. 41
      goals/G-098-suite-tk-tcllib-critcl.md
  2. 11
      src/buildsuites/suite_tcl90/build905.zig
  3. 25
      src/buildsuites/suite_tcl90/critcl_zig.config
  4. 88
      src/buildsuites/suite_tcl90/suite.tcl

41
goals/G-098-suite-tk-tcllib-critcl.md

@ -86,12 +86,49 @@ user direction 2026-07-20:
- SMOKE PASS: in the suite shell, package require Tk + package require tooltip ->
tooltip 2.0.4 (a Tk-dependent tklib package resolving through the aggregate index).
### 2026-07-20 increment 3: tcllib installed; tcllibc built via critcl 3.3.1 + zig cc; 248x hashing evidence
- tcllib pure-tcl side installs via its own installer (same family as tklib: critcl
DSL stubbed, exclusions, aggregating pkgIndex; batch packages-only, suite shell).
INCIDENT: the installer's default libdir derives from the running shell's
tcl_pkgPath, which included the user's machine-global C:/TCLPKGS via inherited
TCLLIBPATH - the first run installed tcllib2.0 THERE. Verified pure additive
(all content fresh-dated, no pre-existing tcllib at that path) and removed.
Fixes: the driver now UNSETS TCLLIBPATH/TCL_LIBRARY/TK_LIBRARY for all child
shells (hermetic; also removes false-positive smoke risk via machine packages),
and both tklib/tcllib installs pass explicit -pkg-path (version parsed from each
project's support/installation/version.tcl). Smoke: md5 2.0.9, sha1 2.0.5,
cmdline 1.5.3 from out/lib/tcllib2.0.
- install-libraries recipe fix surfaced by critcl's package interception: tcl's
installed library/pkgIndex.tcl references subdir packages (msgcat/ opt/
cookiejar/ ...) the piecemeal top-level install never provided - phantom ifneeded
entries. The recipe now installs the whole library tree to lib/tcl9.0
(stock-install parity) alongside the tm installs.
- tcllibc: critcl git-cloned and pinned at tag 3.3.1 under _build (hermetic
git-mirror treatment analogous to the fossil store = noted refinement); tracked
critcl_zig.config overrides critcl's gcc defaults with
`zig cc -target x86_64-windows-gnu` compile/link (driver prepends the pinned zig
dir to PATH). sak.tcl's windows path wants a tclkitsh+starkit combo, so the
driver replicates its underlying invocation directly: critcl main.tcl -config
... -cache ... -force -libdir out/lib -pkg tcllibc + the 21 critcl files parsed
from tcllib's own support/installation/version.tcl declarations (critcl_main
first; brace-grouped lists handled). RESULT: tcllibc.dll built and placed at
out/lib/tcllibc (provide tcllibc 2.0).
- Evidence: package require tcllibc -> 2.0; tcllib md5 auto-engages the critcl
accelerator (accel(critcl)=1). Hashing comparison (punkcheck-relevant): sha1 -hex
over ~5MB in the suite shell - critcl 12.3ms vs pure-tcl 3.06s (pure measured
with tcllibc moved OUTSIDE lib/ - child pkgIndex scanning ignores dir names, so
an in-place rename does not hide a package) = ~248x speedup, confirming the
user-reported punkcheck hashing pain and its fix.
Remaining for acceptance:
- tcllib install via installer.tcl/sak.tcl (behaviour check) + critcl-built tcllibc
(critcl git clone/pin driving zig cc) + hashing comparison evidence.
- suite.tcl test gate: parse all.tcl totals vs a recorded expected-failure baseline;
disposition the 72 current failures.
- punkshell runtests against the suite-built runtime with Tk suites unskipped.
- (superseded note kept for history below: the clone store subsequently moved from
the suite-owned _build location to the machine-level ~/.punkshell/fossils - see
increment 1 and the suite README for current truth)
- Fetch conventions follow suite_tcl90, with the hermetic clone store adopted at this
goal's activation (user decision 2026-07-20): clones live in the suite-owned
_build/suite_tcl90/fossils store (seeded READ-ONLY from ~/.fossils when present,

11
src/buildsuites/suite_tcl90/build905.zig

@ -1818,6 +1818,17 @@ pub fn build(b: *std.Build) !void {
install_libraries.dependOn(&dde_dll.step);
}
//Whole library tree -> lib/tcl9.0 (stock-install parity, G-098 finding: the
//library's own pkgIndex.tcl references subdir packages such as msgcat/ opt/
//cookiejar/ - installing only top-level .tcl files leaves phantom ifneeded
//entries pointing at files that were never installed).
const install_library_tree = b.addInstallDirectory(.{
.source_dir = b.path(tcl_source_folder ++ "/library"),
.install_dir = .prefix,
.install_subdir = "lib/tcl" ++ tcl_dot_version,
});
install_libraries.dependOn(&install_library_tree.step);
//Install library files - non recursive
var library_dir = try std.Io.Dir.cwd().openDir(b.graph.io, tcl_source_folder ++ "/library", .{ .iterate = true });
defer library_dir.close(b.graph.io);

25
src/buildsuites/suite_tcl90/critcl_zig.config

@ -0,0 +1,25 @@
# suite_tcl90 critcl configuration: zig cc as the C toolchain (G-098).
# Overrides critcl's gcc-style defaults; 'zig' is resolved from PATH - the suite
# driver 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).
platform win32-x86_64
compile zig cc -target x86_64-windows-gnu -c -fPIC
version zig version
link zig cc -target x86_64-windows-gnu -shared
include -I
libinclude -L
object .o
output -o [list $outfile]
optimize -O2
noassert -DNDEBUG
tclstubs -DUSE_TCL_STUBS
tkstubs -DUSE_TK_STUBS
debug_symbols -g
strip -Wl,-s
link_debug
link_release
embed_manifest
ldoutput
link_preload
sharedlibext .dll

88
src/buildsuites/suite_tcl90/suite.tcl

@ -132,6 +132,16 @@ file mkdir $stage
set env(FOSSIL_HOME) $stage
file mkdir $fossildir
#Hermetic child shells: the suite-built tclsh must not see the user's machine-level
#package paths (TCLLIBPATH pushed e.g C:/TCLPKGS into tcl_pkgPath here - misdirecting
#installer default paths and risking false-positive smokes via machine packages).
foreach ev {TCLLIBPATH TCL_LIBRARY TK_LIBRARY} {
if {[info exists env($ev)]} {
log "unsetting inherited \$env($ev) for hermetic child shells"
unset env($ev)
}
}
proc fossil_source {name url branch dir} {
#ensure <store>/<name>.fossil exists (read-only seed copy, else network clone) and
#open it at branch in dir
@ -295,11 +305,20 @@ foreach {label path script} [list \
#generation of the aggregating pkgIndex.tcl (gen_main_index). Run it in batch mode
#(packages only) with the suite-built shell; default paths derive from that shell,
#landing the packages under <out>/lib/<tklib-version-dir>.
proc installer_version {dir} {
#tcllib-family: support/installation/version.tcl carries 'package_version <v>'
set f [open [file join $dir support installation version.tcl] r]
set data [read $f]; close $f
if {![regexp {package_version\s+(\S+)} $data -> v]} {error "no package_version in $dir"}
return $v
}
if {![llength [glob -nocomplain [file join $stage out lib tklib*]]]} {
log "installing tklib (batch, packages only) via its own installer"
set tklibver [installer_version [file join $stage tklib]]
log "installing tklib $tklibver (batch, packages only) via its own installer"
set savedpwd [pwd]
cd [file join $stage tklib]
run $exe installer.tcl -no-gui -no-wait -pkgs -no-apps -no-html -no-nroff -no-examples
run $exe installer.tcl -no-gui -no-wait -no-apps -no-html -no-nroff -no-examples \
-pkg-path [file join $stage out lib tklib$tklibver]
cd $savedpwd
}
set tkliblanded [glob -nocomplain [file join $stage out lib tklib*]]
@ -307,4 +326,69 @@ if {![llength $tkliblanded]} {fail "tklib install did not land under [file join
set tksmoke [exec $exe << {package require Tk; wm withdraw .; puts "tooltip: [package require tooltip]"; destroy .; exit}]
log "tklib: [file tail [lindex $tkliblanded 0]] installed; smoke $tksmoke"
#-- tcllib install (G-098) -------------------------------------------------
#Same installer family as tklib (module exclusions + aggregating pkgIndex; critcl DSL
#stubbed to no-ops, so this is the PURE-TCL side only - the critcl-built tcllibc
#accelerators are a separate step). Batch, packages only, suite-built shell.
if {![llength [glob -nocomplain [file join $stage out lib tcllib*]]]} {
set tcllibver [installer_version [file join $stage tcllib]]
log "installing tcllib $tcllibver (batch, packages only, pure-tcl side) via its own installer"
set savedpwd [pwd]
cd [file join $stage tcllib]
run $exe installer.tcl -no-gui -no-wait -no-apps -no-html -no-nroff -no-examples \
-pkg-path [file join $stage out lib tcllib$tcllibver]
cd $savedpwd
}
set tclliblanded [glob -nocomplain [file join $stage out lib tcllib*]]
if {![llength $tclliblanded]} {fail "tcllib install did not land under [file join $stage out lib]"}
set tcllibsmoke [exec $exe << {puts "md5:[package require md5] sha1:[package require sha1] cmdline:[package require cmdline]"; exit}]
log "tcllib: [file tail [lindex $tclliblanded 0]] installed; smoke $tcllibsmoke"
#-- tcllibc accelerators via critcl + zig cc (G-098) -----------------------
#sak.tcl's critcl target expects a tclkitsh+starkit on windows; we replicate its
#underlying invocation directly (critcl -cache ... -force -libdir <dest> -pkg tcllibc
#<module critcl files>) using the critcl source checkout's main.tcl app under the
#suite shell, with the suite's zig on PATH and the tracked critcl_zig.config.
set critcldir [file join $stage critcl]
set critclpin 3.3.1
if {![file isdirectory [file join $critcldir .git]]} {
log "cloning critcl (git) -> $critcldir"
run git clone --quiet https://github.com/andreas-kupries/critcl $critcldir
}
run git -C $critcldir checkout --quiet $critclpin
if {![llength [glob -nocomplain [file join $stage out lib tcllibc*]]]} {
#module list from tcllib's own declarations (support/installation/version.tcl):
#critcl_main's file first, then every critcl module's files (sak.tcl semantics)
set f [open [file join $stage tcllib support installation version.tcl] r]
set vdata [read $f]; close $f
set mainfiles {}
set modfiles {}
foreach line [split $vdata \n] {
if {[regexp {^critcl_main\s+\S+\s+(.+)$} $line -> flist]} {
foreach ff [concat {*}$flist] {lappend mainfiles $ff}
} elseif {[regexp {^critcl\s+\S+\s+(.+)$} $line -> flist]} {
foreach ff [concat {*}$flist] {lappend modfiles $ff}
}
}
if {![llength $mainfiles]} {fail "no critcl_main declaration found in tcllib version.tcl"}
set critclfiles {}
foreach ff [concat $mainfiles $modfiles] {
lappend critclfiles [file join $stage tcllib modules $ff]
}
log "building tcllibc via critcl $critclpin + zig cc ([llength $critclfiles] critcl files)"
set zigdir [file dirname $opt(-zig)]
set savedpath $env(PATH)
set env(PATH) "[file nativename $zigdir];$env(PATH)"
set savedpwd [pwd]
cd [file join $stage tcllib]
run $exe [file join $critcldir main.tcl] \
-config [file join $suiteroot critcl_zig.config] \
-cache [file join $stage .critcl] -force \
-libdir [file join $stage out lib] -pkg tcllibc {*}$critclfiles
cd $savedpwd
set env(PATH) $savedpath
}
set tcllibcsmoke [exec $exe << {puts "tcllibc:[package require tcllibc]"; package require md5; puts "md5-critcl-accel:$::md5::accel(critcl)"; exit}]
log "tcllibc: $tcllibcsmoke"
log "PASS - built $patchlevel (checkout [string range $uuid 0 11]..., zig $zigv) -> $stage/out"

Loading…
Cancel
Save