From 440bd029d8352ab69bd2665d5d28ac1f4475077e Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Mon, 20 Jul 2026 06:44:30 +1000 Subject: [PATCH] G-098 increment 1: hermetic fossil clone store (~/.punkshell/fossils) + Tk extension builds and smokes Clone dbs are caches, not build output: punkshell-owned machine-level store at ~/.punkshell/fossils (independent of personal ~/.fossils, outside deletable _build dirs), read-only seeding from ~/.fossils, -repofolder / PUNK_FOSSIL_STORE override (registered in punk::config for 'help env'), -refresh pulls clones. Verified seed->open->build->smoke from empty store, then migrated + re-verified. Sources wired: tk core-9-0-branch (9.0.2, stale clone pulled current), tklib + tcllib trunk. build_tk/build_tk.zig (0.16 API): tcl9tk90.dll from stock sources per makefile.vc object inventory (+ xlib emulation set), ansi/UNICODE tkMain pair with forced -U ansi guard, CFG_* defines, win/rc/tk.rc with explicit rc include paths, oleaut32 et al; tk library -> lib/tk9.0 with driver-generated tkUuid.h, wish.exe.manifest and pkgIndex.tcl. SMOKE PASS: package require Tk -> 9.0.2, screen queried, widget packed/updated, clean destroy, in the suite-built tclsh90s. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- GOALS.md | 2 +- goals/G-098-suite-tk-tcllib-critcl.md | 57 ++++++- .../samplesuite1/download_and_build.config | 4 +- src/buildsuites/suite_tcl90/README.md | 11 +- src/buildsuites/suite_tcl90/build905.zig | 8 + .../suite_tcl90/build_tk/build_tk.zig | 153 ++++++++++++++++++ src/buildsuites/suite_tcl90/suite.tcl | 106 ++++++++++-- src/modules/punk/config-999999.0a1.0.tm | 1 + src/modules/punk/config-buildversion.txt | 1 + 9 files changed, 325 insertions(+), 18 deletions(-) create mode 100644 src/buildsuites/suite_tcl90/build_tk/build_tk.zig diff --git a/GOALS.md b/GOALS.md index 2c2e7a36..a4d12cc1 100644 --- a/GOALS.md +++ b/GOALS.md @@ -350,6 +350,6 @@ Detail: goals/G-096-zig-buildsuite-piperepl.md Scope: src/scriptapps/ + src/scriptapps/bin/ (bits, runtime, tclargs scriptset renames; selfsign experiment scripts), bin/ (regenerated wrapper twins + renamed experiment scripts, old names retired), src/tests/modules/punk/mix/testsuites/scriptwrap/ + src/tests/shell/testsuites/binscripts/ (roundtrip pins), bin/AGENTS.md + referencing docs Detail: goals/G-097-bin-utility-naming-sweep.md -### G-098 [proposed] suite_tcl90 full punkshell dependency set: Tk+tklib, tcllib with critcl accelerators, and testsuite gating +### G-098 [active] suite_tcl90 full punkshell dependency set: Tk+tklib, tcllib with critcl accelerators, and testsuite gating Scope: src/buildsuites/suite_tcl90/ (recipe extensions, driver steps, testsuite gate), 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 runtime) Detail: goals/G-098-suite-tk-tcllib-critcl.md diff --git a/goals/G-098-suite-tk-tcllib-critcl.md b/goals/G-098-suite-tk-tcllib-critcl.md index 9a21c00a..a5bd89e9 100644 --- a/goals/G-098-suite-tk-tcllib-critcl.md +++ b/goals/G-098-suite-tk-tcllib-critcl.md @@ -1,6 +1,6 @@ # G-098 suite_tcl90 full punkshell dependency set: Tk+tklib, tcllib with critcl accelerators, and testsuite gating -Status: proposed +Status: active Scope: src/buildsuites/suite_tcl90/ (recipe extensions, driver steps, testsuite gate), 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 runtime) Goal: suite_tcl90 builds/installs the remaining runtime pieces punkshell testing depends on - Tk (fossil core.tcl-lang.org/tk) loadable by the suite's Tcl 9.0 windows runtime together with a matching tklib (fossil core.tcl-lang.org/tklib; pure-tcl, installed via its own installer.tcl where that does more than a straight copy), and tcllib (fossil core.tcl-lang.org/tcllib) installed via its documented mechanism (installer.tcl / sak.tcl checked for installation behaviour that is not just a straight copy) WITH its critcl-built C accelerators (tcllibc; critcl from github.com/andreas-kupries/critcl driving zig cc as the compiler, bootstrapped by the suite-built tclsh - no MS toolchain) - and the suite's test surface becomes a gate: suite.tcl test parses the all.tcl totals rather than trusting the exit code and fails on failures absent from a recorded expected-failure baseline. Acceptance: from the pinned zig and fetched sources the suite produces a Tk the suite tclsh loads (package require Tk with a create/destroy-window verification recorded in this file) and a matching tklib whose packages resolve from the install (a representative tklib package require recorded); a tcllib install performed via its own installer machinery (installer.tcl/sak.tcl behaviour checked and recorded - module selection, pkgIndex/tm generation, critcl module handling) whose accelerators load (package require tcllibc) with a punkcheck-relevant hashing comparison showing the accelerated path engaged versus pure-tcl; critcl is version-pinned and driven with zig cc; suite.tcl test gates on parsed totals against a recorded expected-failure baseline and the current 72 core-test failures are dispositioned (fixed or baselined with reasons); punkshell's runtests can run against the suite-built runtime with Tk-dependent suites no longer skipped for lack of Tk. @@ -34,7 +34,54 @@ user direction 2026-07-20: consumer), G-021/G-020 (agent visual verification - a working Tk underpins those). - The 72-failure triage is tracked HERE (gate clause), not in G-096, to avoid double tracking. -- Fetch conventions follow suite_tcl90: fossil clones cached in ~/.fossils, checkouts - under src/buildsuites/_build with FOSSIL_HOME isolation and --nested; critcl is git, - not fossil - clone/pin convention to be settled in the work (git clone under _build - with a pinned tag/commit is the expected shape). + +## Progress + +### 2026-07-20 increment 1: activated; hermetic clone store; Tk builds and smokes + +- Activated by the user. Clone-store design (two user course-checks): fossil clone dbs + are CACHES, not build output - they now live in the punkshell-owned machine-level + store ~/.punkshell/fossils (user preference: ~/... on all platforms for + cross-platform doc simplicity, a deliberate exception to windows convention), + independent of the personal ~/.fossils AND outside deletable _build-class dirs + (git clean -fdx safe). Missing clones seed READ-ONLY by file copy from -seedfossils + (default ~/.fossils; the seed is never written - the copy is pulled current), else + network-clone. Override: -repofolder / PUNK_FOSSIL_STORE, the latter registered in + punk::config's known env vars ('help env' surfacing; punk::config pending-0.2.0 + entry). -refresh now pulls store clones before updating checkouts; clean wipes the + stage only. Verified: full seed->open->build->smoke PASS from an empty store (6 + clones seeded, ~1.2GB), then store migrated to ~/.punkshell/fossils and re-verified + (checkouts re-opened from the new store, PASS, zero re-seeds). +- Sources wired: tk (core-9-0-branch -> stage/tk9; clone was stale at 2023/8.7a6 and + pulled current - TK 9.0.2), tklib (trunk, current), tcllib (trunk, current). +- Tk recipe (build_tk/build_tk.zig, 0.16 API): tcl9tk90.dll from stock sources - + object inventory from tk/win/makefile.vc TKOBJS+TTK_OBJS (generic/unix-shared/win/ + ttk splits corrected against the tree; xlib emulation sources x{colors,draw,gc, + image,util}.c were absent from the name-pattern extraction and restored; + tkStubLib/ttkStubLib excluded as stub-archive objects), tkMain compiled ansi+UNICODE + with the forced -U ansi guard (G-096 finding), CFG_* pkgconfig defines generated at + build time, rc from win/rc/tk.rc with explicit include_paths (wish.exe.manifest and + tcl headers), system libs incl oleaut32 (tkWinSend OLE). tk script library installs + to out/lib/tk9.0; the driver generates tkUuid.h + wish.exe.manifest (TK_WIN_VERSION + 9.0.2.902 by the tree's own formula) and writes the pkgIndex.tcl (tk + Tk ifneeded + -> ../../bin/tcl9tk90.dll, makefile.vc install convention). +- SMOKE PASS on the suite shell: package require Tk -> 9.0.2; winfo screen + 3840x2160 depth 32; label pack + update + destroy clean (real window station + exercised, not just a load). + +Remaining for acceptance: + +- tklib install via its installer.tcl (behaviour check first) + representative + package require evidence. +- 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. +- 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, + else network-cloned; -repofolder/PUNK_FOSSIL_STORE override; clean keeps the store, + clean -all removes it), checkouts under _build with FOSSIL_HOME isolation and + --nested. critcl is git, not fossil - clone/pin convention to be settled in the work + (git clone under _build with a pinned tag/commit is the expected shape). diff --git a/src/buildsuites/samplesuite1/download_and_build.config b/src/buildsuites/samplesuite1/download_and_build.config index 5917f757..ebcbfedc 100644 --- a/src/buildsuites/samplesuite1/download_and_build.config +++ b/src/buildsuites/samplesuite1/download_and_build.config @@ -16,7 +16,9 @@ set buildprefix /src/buildsuites/_build/samplesuite1 set basemakeflags [list -j 2] set baseconfigflags [list --enable-64bit --prefix=$buildprefix] -set repofolder ~/.fossils +#repofolder convention superseded (G-098, 2026-07-20): suites use a suite-owned store +#under the _build area (see suite_tcl90 - hermetic, read-only seeding from ~/.fossils) +set repofolder /src/buildsuites/_build/samplesuite1/fossils #------------------------------------------------------- diff --git a/src/buildsuites/suite_tcl90/README.md b/src/buildsuites/suite_tcl90/README.md index 374438fd..959b68b6 100644 --- a/src/buildsuites/suite_tcl90/README.md +++ b/src/buildsuites/suite_tcl90/README.md @@ -67,8 +67,15 @@ producing stale/duplicate artifacts at link time). | `tclthread` | fossil `https://core.tcl-lang.org/thread` | trunk | | `tclvfs` | fossil `https://core.tcl-lang.org/tclvfs` | trunk | -Checkouts under `_build` use `FOSSIL_HOME=` so the user's global fossil config -db records nothing (stage-local `_fossil` db, deleted with `clean`). +Fossil clones live in a SUITE-OWNED per-checkout store, `_build/suite_tcl90/fossils/` +(hermetic - the build system is independent of any personal fossil collection; +G-098 decision 2026-07-20). Missing clones are seeded by READ-ONLY file copy from a +local collection when available (`-seedfossils`, default `~/.fossils` - the seed is +never written; the copy is pulled current), else network-cloned. Store override: +`-repofolder` or `PUNK_FOSSIL_STORE`. `-refresh 1` pulls the store clones and updates +checkouts. Checkouts use `FOSSIL_HOME=` so the user's global fossil config db +records nothing (stage-local `_fossil` db). `clean` keeps the clone store; `clean +-all 1` removes it too. ## Fresh-checkout preparations the driver performs diff --git a/src/buildsuites/suite_tcl90/build905.zig b/src/buildsuites/suite_tcl90/build905.zig index b54debe6..0fc62784 100644 --- a/src/buildsuites/suite_tcl90/build905.zig +++ b/src/buildsuites/suite_tcl90/build905.zig @@ -14,6 +14,7 @@ const tclvfs_shared = @import("build_tclvfs/build_tclvfs_shared.zig"); //compile site and TEMP_REFERENCE/metakit + TEMP_REFERENCE/KitCreator for future work) const build_tclthread = @import("build_tclthread/build_tclthread.zig").build_tclthread; +const build_tk = @import("build_tk/build_tk.zig").build_tk; //G-098 const tcl_major_version = "9"; const tcl_nodot_version = "90"; //VER in makefile @@ -2036,6 +2037,13 @@ pub fn build(b: *std.Build) !void { build_tclthread_step.dependOn(&thread_lib_compile.step); b.getInstallStep().dependOn(build_tclthread_step); + //G-098: Tk loadable extension (windows target only for now) + if (target.result.os.tag == .windows) { + const tk_lib_compile = try build_tk(tcl_source_folder, "../tk9", b, target, optimize, finalstublib); + const build_tk_step = b.step("build-tk", "build tk shared library (tcl9tk90.dll) + tk script library install"); + build_tk_step.dependOn(&tk_lib_compile.step); + } + //if !ZIPFS_BUILD //install without zipfs //b.getInstallStep().dependOn(install_libraries); diff --git a/src/buildsuites/suite_tcl90/build_tk/build_tk.zig b/src/buildsuites/suite_tcl90/build_tk/build_tk.zig new file mode 100644 index 00000000..719730e9 --- /dev/null +++ b/src/buildsuites/suite_tcl90/build_tk/build_tk.zig @@ -0,0 +1,153 @@ +const std = @import("std"); + +//G-098: Tk 9.0 windows loadable extension (tcl9tk90.dll) from stock sources, plus the +//tk script library installed to lib/tk9.0. Object inventory derived from +//tk/win/makefile.vc TKOBJS + TTK_OBJS (tk 9.0.2 era); tkStubLib/ttkStubLib are the +//stub-consumer archive objects and deliberately NOT compiled into the dll. +//tkMain.c is compiled twice like tcl's tclMain (ansi + UNICODE 'tkMain2' per the +//makefile); the ansi copy gets explicit -UUNICODE/-U_UNICODE so the zig cc driver's +//windows defaults cannot turn it into a duplicate UNICODE compile (G-096 finding). + +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", + "tkIcu", "tkImage", "tkImgBmap", "tkImgListFormat", "tkImgGIF", + "tkImgPNG", "tkImgPPM", "tkImgSVGnano", "tkImgPhoto", "tkImgPhInstance", + "tkImgUtil", "tkListbox", "tkMacWinMenu", "tkMenu", "tkMenubutton", + "tkMenuDraw", "tkMessage", "tkPanedWindow", "tkObj", "tkOldConfig", + "tkOption", "tkPack", "tkPkgConfig", "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", "tkWinGDI", "tkWinIco", "tkWinImage", "tkWinInit", + "tkWinKey", "tkWinMenu", "tkWinPixmap", "tkWinPointer", "tkWinRegion", + "tkWinScrlbr", "tkWinSend", "tkWinSendCom", "tkWinSysTray", "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 fn build_tk(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, stublib: *std.Build.Step.Compile) !*std.Build.Step.Compile { + const lib = b.addLibrary(.{ + .linkage = .dynamic, + .name = "tcl9tk90", + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), + }); + lib.mingw_unicode_entry_point = true; //parity with the tclsh exe environment + + var flags = std.array_list.Managed([]const u8).init(b.allocator); + defer flags.deinit(); + try flags.appendSlice(&.{ + "-Wall", + "-O3", + "-fomit-frame-pointer", + "-fno-sanitize=undefined", + "-DBUILD_tk=/**/", + "-DBUILD_ttk=/**/", + "-DUSE_TCL_STUBS=1", + "-DTCL_CFG_OPTIMIZED=1", + "-DHAVE_NO_SEH=1", + }); + //tkPkgConfig.c wants the configure-era CFG_* values (parity with the tcl side's + //tclPkgConfig handling in build905.zig) + const prefix = b.install_path; + const tk_script_dir = b.pathJoin(&.{ prefix, "lib", "tk9.0" }); + inline for (.{ "RUNTIME", "INSTALL" }) |cls| { + try flags.append(try std.fmt.allocPrint(b.allocator, "-DCFG_{s}_LIBDIR=\"{s}\"", .{ cls, b.pathJoin(&.{ prefix, "lib" }) })); + try flags.append(try std.fmt.allocPrint(b.allocator, "-DCFG_{s}_BINDIR=\"{s}\"", .{ cls, b.pathJoin(&.{ prefix, "bin" }) })); + try flags.append(try std.fmt.allocPrint(b.allocator, "-DCFG_{s}_SCRDIR=\"{s}\"", .{ cls, tk_script_dir })); + try flags.append(try std.fmt.allocPrint(b.allocator, "-DCFG_{s}_INCDIR=\"{s}\"", .{ cls, b.pathJoin(&.{ prefix, "include" }) })); + try flags.append(try std.fmt.allocPrint(b.allocator, "-DCFG_{s}_DOCDIR=\"{s}\"", .{ cls, b.pathJoin(&.{ prefix, "man" }) })); + } + try flags.append("-DCFG_RUNTIME_DLLFILE=\"tcl9tk90.dll\""); + + 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"); + + 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")); + lib.root_module.addIncludePath(b.path(subdir ++ "/bitmaps")); + 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, + }); + + lib.root_module.addWin32ResourceFile(.{ + .file = b.path(subdir ++ "/win/rc/tk.rc"), + .include_paths = &.{ + 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; + inline for (.{ "netapi32", "gdi32", "user32", "userenv", "winspool", "shell32", "ole32", "oleaut32", "uuid", "comctl32", "comdlg32", "imm32", "uxtheme", "advapi32" }) |l| { + lib.root_module.linkSystemLibrary(l, .{}); + } + + b.installArtifact(lib); + + //tk script library -> /lib/tk9.0 (pkgIndex.tcl is generated by the suite + //driver post-build, per the makefile.vc install-binaries convention) + const install_tklib = b.addInstallDirectory(.{ + .source_dir = b.path(subdir ++ "/library"), + .install_dir = .prefix, + .install_subdir = "lib/tk9.0", + }); + b.getInstallStep().dependOn(&install_tklib.step); + + return lib; +} diff --git a/src/buildsuites/suite_tcl90/suite.tcl b/src/buildsuites/suite_tcl90/suite.tcl index bbcd4694..8e657824 100644 --- a/src/buildsuites/suite_tcl90/suite.tcl +++ b/src/buildsuites/suite_tcl90/suite.tcl @@ -10,12 +10,22 @@ # -zig zig executable (default: $env(PUNK_ZIG), else 'zig' on PATH) # -optimize Debug|ReleaseSafe|ReleaseFast|ReleaseSmall (default ReleaseFast) # -tclbranch tcl branch/tag to open (default core-9-0-branch) -# -refresh <0|1> fossil update existing source checkouts (default 0) +# -refresh <0|1> pull clones + fossil update existing source checkouts (default 0) +# -repofolder fossil clone store (default ~/.punkshell/fossils; env PUNK_FOSSIL_STORE) +# -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 (default {install install-libraries make-zipfs}) # #The build area lives entirely under src/buildsuites/_build (VCS-ignored via the -#existing _build globs). Fossil clones are cached in ~/.fossils; checkouts made here -#use FOSSIL_HOME= so the user's global fossil config db records nothing. +#existing _build globs). Fossil clones live in the PUNKSHELL-OWNED machine-level +#store ~/.punkshell/fossils (G-098 decisions 2026-07-20: independent of any personal +#fossil collection AND outside deletable _build-class dirs - clone dbs are caches, +#not build output). Missing clones are seeded READ-ONLY by file copy from a local +#collection when available (-seedfossils, default ~/.fossils; the seed is never +#written - the copy is pulled current), else network-cloned. Store override: +#-repofolder or PUNK_FOSSIL_STORE (punk::config-registered - see 'help env'). +#Checkouts use FOSSIL_HOME= so the user's global fossil config db records +#nothing. 'clean'/'clean -all 1' wipe the stage; the store lives outside it. proc log {msg} {puts stdout "suite_tcl90: $msg"; flush stdout} proc fail {msg} {puts stderr "suite_tcl90 ERROR: $msg"; flush stderr; exit 1} @@ -34,7 +44,6 @@ proc run {args} { set suiteroot [file dirname [file normalize [info script]]] set stage [file normalize [file join $suiteroot .. _build suite_tcl90]] -set fossildir [file normalize [file join $env(HOME) .fossils]] #-- options --------------------------------------------------------------- set action [lindex $argv 0] @@ -48,7 +57,10 @@ array set opt { -refresh 0 -steps {install install-libraries make-zipfs} -testargs {} + -repofolder {} + -all 0 } +set opt(-seedfossils) [file normalize [file join $env(HOME) .fossils]] #zig resolution: -zig option > PUNK_ZIG env > the suite's pinned toolchain under #bin/tools (zig 0.16.0 official release - hosted on punkbin, minisign-verified; #the recipe is 0.16-API and does NOT build under 0.14/0.15 - see README Lineage) @@ -66,8 +78,31 @@ foreach {k v} [lrange $argv 1 end] { set opt($k) $v } +#suite-owned clone store resolution: -repofolder > PUNK_FOSSIL_STORE > ~/.punkshell/fossils +#The default is a punkshell-owned MACHINE-LEVEL cache outside the repo tree: _build +#class dirs are idiomatically deletable (git clean -fdx included) and clone dbs are +#caches, not build output. ~/... on all platforms is a deliberate user preference +#(cross-platform doc simplicity) despite windows convention. PUNK_FOSSIL_STORE is a +#punk::config-registered env var ('help env' documents it in punkshell). +if {$opt(-repofolder) ne ""} { + set fossildir [file normalize $opt(-repofolder)] +} elseif {[info exists env(PUNK_FOSSIL_STORE)] && $env(PUNK_FOSSIL_STORE) ne ""} { + set fossildir [file normalize $env(PUNK_FOSSIL_STORE)] +} else { + if {[info exists env(HOME)] && $env(HOME) ne ""} { + set homedir $env(HOME) + } elseif {[info exists env(USERPROFILE)] && $env(USERPROFILE) ne ""} { + set homedir $env(USERPROFILE) + } elseif {![catch {file home} h]} { + set homedir $h + } else { + fail "cannot determine home directory for the default fossil store (set PUNK_FOSSIL_STORE or -repofolder)" + } + set fossildir [file join [file normalize $homedir] .punkshell fossils] +} + if {$action eq "clean"} { - log "removing $stage" + log "removing $stage (the clone store at $fossildir is untouched)" file delete -force $stage exit 0 } @@ -98,12 +133,26 @@ set env(FOSSIL_HOME) $stage file mkdir $fossildir proc fossil_source {name url branch dir} { - #ensure ~/.fossils/.fossil exists (clone if needed) and open it at branch in dir + #ensure /.fossil exists (read-only seed copy, else network clone) and + #open it at branch in dir global fossildir opt set repo [file join $fossildir $name.fossil] if {![file exists $repo]} { - log "cloning $url -> $repo" - run fossil clone $url $repo + set seeded 0 + if {$opt(-seedfossils) ne ""} { + set seed [file join $opt(-seedfossils) $name.fossil] + if {[file exists $seed]} { + log "seeding $name.fossil into the suite store from $seed (read-only copy)" + file copy $seed $repo + #the seed may be stale - freshen OUR copy (the seed is never written) + run fossil pull -R $repo + set seeded 1 + } + } + if {!$seeded} { + log "cloning $url -> $repo" + run fossil clone $url $repo + } } if {![file exists [file join $dir _FOSSIL_]] && ![file exists [file join $dir .fslckout]]} { file mkdir $dir @@ -115,6 +164,7 @@ proc fossil_source {name url branch dir} { } elseif {$opt(-refresh)} { set savedpwd [pwd] cd $dir + run fossil pull -R $repo run fossil update $branch cd $savedpwd } @@ -127,7 +177,7 @@ proc fossil_source {name url branch dir} { set builddir [file join $stage build] log "staging recipe -> $builddir" file mkdir $builddir -foreach item {build905.zig build_libtommath build_zlib build_tclvfs build_tclthread src tools} { +foreach item {build905.zig build_libtommath build_zlib build_tclvfs build_tclthread build_tk src tools} { file delete -force [file join $builddir $item] file copy [file join $suiteroot $item] $builddir } @@ -136,6 +186,11 @@ foreach item {build905.zig build_libtommath build_zlib build_tclvfs build_tclthr fossil_source tcl https://core.tcl-lang.org/tcl $opt(-tclbranch) [file join $stage tcl905] fossil_source tclthread https://core.tcl-lang.org/thread trunk [file join $stage tclthread] fossil_source tclvfs https://core.tcl-lang.org/tclvfs trunk [file join $stage tclvfs] +#G-098: Tk + companion script libraries. tklib is pure-tcl (installed by its own +#installer where that matters); tcllib's accelerators (tcllibc) are built via critcl. +fossil_source tk https://core.tcl-lang.org/tk core-9-0-branch [file join $stage tk9] +fossil_source tklib https://core.tcl-lang.org/tklib trunk [file join $stage tklib] +fossil_source tcllib https://core.tcl-lang.org/tcllib trunk [file join $stage tcllib] #zlib: use the tcl tree's own vendored copy (version-matched; includes contrib/minizip) set zlibdir [file join $stage zlib] @@ -179,6 +234,27 @@ if {![string match "*tcl905/generic/tcl.h*" $rc]} { log "thread.rc: inserted tcl.h include for the version resource" } +#tk generated files (G-098): tkUuid.h from manifest.uuid; wish.exe.manifest from its +#.in (tk.rc depends on it) - same configure-product class as the tcl ones above. +set tkdir [file join $stage tk9] +set f [open [file join $tkdir manifest.uuid] r]; set tkuuid [read $f]; close $f +set f [open [file join $tkdir win tkUuid.h] w] +fconfigure $f -translation lf +puts -nonewline $f "#define TK_VERSION_UUID \\\n$tkuuid" +close $f +set f [open [file join $tkdir generic tk.h] r]; set tkh [read $f]; close $f +if {![regexp {#\s*define\s+TK_VERSION\s+"([^"]+)"} $tkh -> tkversion]} {fail "TK_VERSION not found in tk.h"} +if {![regexp {#\s*define\s+TK_PATCH_LEVEL\s+"([^"]+)"} $tkh -> tkpatchlevel]} {fail "TK_PATCH_LEVEL not found in tk.h"} +set tkrlevel [expr {[string match *a* $tkpatchlevel] ? 0 : ([string match *b* $tkpatchlevel] ? 1 : 2)}] +set tkwinver "$tkversion.$tkrlevel.[string map {a {} b {} . {}} $tkpatchlevel]" +set f [open [file join $tkdir win wish.exe.manifest.in] r]; set m [read $f]; close $f +set m [string map [list @TK_WIN_VERSION@ $tkwinver @MACHINE@ AMD64] $m] +set f [open [file join $tkdir win wish.exe.manifest] w] +fconfigure $f -translation lf +puts -nonewline $f $m +close $f +log "tk prepared (patchlevel $tkpatchlevel, TK_WIN_VERSION $tkwinver)" + #-- build ----------------------------------------------------------------- set zigv [exec $opt(-zig) version] log "zig: $opt(-zig) ($zigv)" @@ -190,6 +266,18 @@ cd $builddir run $opt(-zig) build --build-file build905.zig --cache-dir $cachedir --prefix [file join $stage out] -Doptimize=$opt(-optimize) {*}$opt(-steps) cd $savedpwd +#-- tk pkgIndex (makefile.vc install-binaries convention) ------------------ +set tkpkgdir [file join $stage out lib tk$tkversion] +if {[file isdirectory $tkpkgdir]} { + set f [open [file join $tkpkgdir pkgIndex.tcl] w] + fconfigure $f -translation lf + puts $f "if {\[catch {package present Tcl 9.0-}\]} return" + puts $f "package ifneeded tk $tkpatchlevel \[list load \[file normalize \[file join \$dir .. .. bin tcl9tk90.dll\]\]\]" + puts $f "package ifneeded Tk $tkpatchlevel \[list load \[file normalize \[file join \$dir .. .. bin tcl9tk90.dll\]\]\]" + close $f + log "tk pkgIndex.tcl written ($tkpkgdir)" +} + #-- smoke ----------------------------------------------------------------- set exe [file join $stage out bin tclsh90s.exe] set zexe [file join $stage out bin tclsh90szip.exe] diff --git a/src/modules/punk/config-999999.0a1.0.tm b/src/modules/punk/config-999999.0a1.0.tm index 73d6af25..a5d9318f 100644 --- a/src/modules/punk/config-999999.0a1.0.tm +++ b/src/modules/punk/config-999999.0a1.0.tm @@ -275,6 +275,7 @@ tcl::namespace::eval punk::config { PUNK_APPS {type pathlist} PUNK_CONFIG {type string} PUNK_CONFIGSET {type string} + PUNK_FOSSIL_STORE {type string help "location of the punkshell-owned fossil clone store\n(source mirrors for buildsuites - e.g src/buildsuites/suite_tcl90/suite.tcl).\nDefault: ~/.punkshell/fossils (machine-level cache, outside deletable _build areas).\nMissing clones are seeded read-only from ~/.fossils when present, else network-cloned."} PUNK_SCRIPTLIB {type string} PUNK_AUTO_EXEC_MECHANISM {type string} PUNK_AUTO_NOEXEC {type string default 0 help "set 1 to set Tcl's ::auto_noexec true.\nStops 'unknown' from running external programs"} diff --git a/src/modules/punk/config-buildversion.txt b/src/modules/punk/config-buildversion.txt index f77810e2..aafa36b4 100644 --- a/src/modules/punk/config-buildversion.txt +++ b/src/modules/punk/config-buildversion.txt @@ -1,6 +1,7 @@ 0.2.0 #First line must be a semantic version number #all other lines are ignored. +#0.2.0 - G-098: registered PUNK_FOSSIL_STORE in the known punk env vars ('help env' surfacing) - overrides the punkshell-owned fossil clone store location used by buildsuites (default ~/.punkshell/fossils, machine-level cache outside deletable _build areas; read-only seeding from ~/.fossils). #0.2.0 - registered PUNK_TEST_UDPTEE in the known punk env vars (runtests UDP watch mode / runx -teelog - see src/tests/AGENTS.md) #0.2.0 - converted from manually-versioned config-0.1.tm to standard buildversion tracking (magic 999999.0a1.0 source filename; punk::config was never in src/modules/AGENTS.md's manual-versioning exception list) #0.1 - prior manually-versioned baseline (filename config-0.1.tm, no buildversion.txt)