Browse Source

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
master
Julian Noble 2 weeks ago
parent
commit
440bd029d8
  1. 2
      GOALS.md
  2. 57
      goals/G-098-suite-tk-tcllib-critcl.md
  3. 4
      src/buildsuites/samplesuite1/download_and_build.config
  4. 11
      src/buildsuites/suite_tcl90/README.md
  5. 8
      src/buildsuites/suite_tcl90/build905.zig
  6. 153
      src/buildsuites/suite_tcl90/build_tk/build_tk.zig
  7. 106
      src/buildsuites/suite_tcl90/suite.tcl
  8. 1
      src/modules/punk/config-999999.0a1.0.tm
  9. 1
      src/modules/punk/config-buildversion.txt

2
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 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 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) 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 Detail: goals/G-098-suite-tk-tcllib-critcl.md

57
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 # 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) 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. 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. 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). 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 - The 72-failure triage is tracked HERE (gate clause), not in G-096, to avoid double
tracking. 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, ## Progress
not fossil - clone/pin convention to be settled in the work (git clone under _build
with a pinned tag/commit is the expected shape). ### 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).

4
src/buildsuites/samplesuite1/download_and_build.config

@ -16,7 +16,9 @@
set buildprefix <PROJECTDIR>/src/buildsuites/_build/samplesuite1 set buildprefix <PROJECTDIR>/src/buildsuites/_build/samplesuite1
set basemakeflags [list -j 2] set basemakeflags [list -j 2]
set baseconfigflags [list --enable-64bit --prefix=$buildprefix] 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 <PROJECTDIR>/src/buildsuites/_build/samplesuite1/fossils
#------------------------------------------------------- #-------------------------------------------------------

11
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 | | `tclthread` | fossil `https://core.tcl-lang.org/thread` | trunk |
| `tclvfs` | fossil `https://core.tcl-lang.org/tclvfs` | trunk | | `tclvfs` | fossil `https://core.tcl-lang.org/tclvfs` | trunk |
Checkouts under `_build` use `FOSSIL_HOME=<stage>` so the user's global fossil config Fossil clones live in a SUITE-OWNED per-checkout store, `_build/suite_tcl90/fossils/`
db records nothing (stage-local `_fossil` db, deleted with `clean`). (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=<stage>` 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 ## Fresh-checkout preparations the driver performs

8
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) //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_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_major_version = "9";
const tcl_nodot_version = "90"; //VER in makefile 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); build_tclthread_step.dependOn(&thread_lib_compile.step);
b.getInstallStep().dependOn(build_tclthread_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 //if !ZIPFS_BUILD
//install without zipfs //install without zipfs
//b.getInstallStep().dependOn(install_libraries); //b.getInstallStep().dependOn(install_libraries);

153
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 -> <prefix>/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;
}

106
src/buildsuites/suite_tcl90/suite.tcl

@ -10,12 +10,22 @@
# -zig <path> zig executable (default: $env(PUNK_ZIG), else 'zig' on PATH) # -zig <path> zig executable (default: $env(PUNK_ZIG), else 'zig' on PATH)
# -optimize <mode> Debug|ReleaseSafe|ReleaseFast|ReleaseSmall (default ReleaseFast) # -optimize <mode> Debug|ReleaseSafe|ReleaseFast|ReleaseSmall (default ReleaseFast)
# -tclbranch <branch> tcl branch/tag to open (default core-9-0-branch) # -tclbranch <branch> 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 <dir> fossil clone store (default ~/.punkshell/fossils; env PUNK_FOSSIL_STORE)
# -seedfossils <dir> read-only seed collection for missing clones (default ~/.fossils; "" disables)
# -all <0|1> with 'clean': reserved (the clone store lives outside the stage)
# -steps <list> zig build steps (default {install install-libraries make-zipfs}) # -steps <list> zig build steps (default {install install-libraries make-zipfs})
# #
#The build area lives entirely under src/buildsuites/_build (VCS-ignored via the #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 #existing _build globs). Fossil clones live in the PUNKSHELL-OWNED machine-level
#use FOSSIL_HOME=<stage> so the user's global fossil config db records nothing. #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=<stage> 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 log {msg} {puts stdout "suite_tcl90: $msg"; flush stdout}
proc fail {msg} {puts stderr "suite_tcl90 ERROR: $msg"; flush stderr; exit 1} 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 suiteroot [file dirname [file normalize [info script]]]
set stage [file normalize [file join $suiteroot .. _build suite_tcl90]] set stage [file normalize [file join $suiteroot .. _build suite_tcl90]]
set fossildir [file normalize [file join $env(HOME) .fossils]]
#-- options --------------------------------------------------------------- #-- options ---------------------------------------------------------------
set action [lindex $argv 0] set action [lindex $argv 0]
@ -48,7 +57,10 @@ array set opt {
-refresh 0 -refresh 0
-steps {install install-libraries make-zipfs} -steps {install install-libraries make-zipfs}
-testargs {} -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 #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; #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) #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 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"} { if {$action eq "clean"} {
log "removing $stage" log "removing $stage (the clone store at $fossildir is untouched)"
file delete -force $stage file delete -force $stage
exit 0 exit 0
} }
@ -98,12 +133,26 @@ set env(FOSSIL_HOME) $stage
file mkdir $fossildir file mkdir $fossildir
proc fossil_source {name url branch dir} { proc fossil_source {name url branch dir} {
#ensure ~/.fossils/<name>.fossil exists (clone if needed) and open it at branch in dir #ensure <store>/<name>.fossil exists (read-only seed copy, else network clone) and
#open it at branch in dir
global fossildir opt global fossildir opt
set repo [file join $fossildir $name.fossil] set repo [file join $fossildir $name.fossil]
if {![file exists $repo]} { if {![file exists $repo]} {
log "cloning $url -> $repo" set seeded 0
run fossil clone $url $repo 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]]} { if {![file exists [file join $dir _FOSSIL_]] && ![file exists [file join $dir .fslckout]]} {
file mkdir $dir file mkdir $dir
@ -115,6 +164,7 @@ proc fossil_source {name url branch dir} {
} elseif {$opt(-refresh)} { } elseif {$opt(-refresh)} {
set savedpwd [pwd] set savedpwd [pwd]
cd $dir cd $dir
run fossil pull -R $repo
run fossil update $branch run fossil update $branch
cd $savedpwd cd $savedpwd
} }
@ -127,7 +177,7 @@ proc fossil_source {name url branch dir} {
set builddir [file join $stage build] set builddir [file join $stage build]
log "staging recipe -> $builddir" log "staging recipe -> $builddir"
file mkdir $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 delete -force [file join $builddir $item]
file copy [file join $suiteroot $item] $builddir 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 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 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] 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) #zlib: use the tcl tree's own vendored copy (version-matched; includes contrib/minizip)
set zlibdir [file join $stage zlib] 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" 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 ----------------------------------------------------------------- #-- build -----------------------------------------------------------------
set zigv [exec $opt(-zig) version] set zigv [exec $opt(-zig) version]
log "zig: $opt(-zig) ($zigv)" 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) run $opt(-zig) build --build-file build905.zig --cache-dir $cachedir --prefix [file join $stage out] -Doptimize=$opt(-optimize) {*}$opt(-steps)
cd $savedpwd 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 ----------------------------------------------------------------- #-- smoke -----------------------------------------------------------------
set exe [file join $stage out bin tclsh90s.exe] set exe [file join $stage out bin tclsh90s.exe]
set zexe [file join $stage out bin tclsh90szip.exe] set zexe [file join $stage out bin tclsh90szip.exe]

1
src/modules/punk/config-999999.0a1.0.tm

@ -275,6 +275,7 @@ tcl::namespace::eval punk::config {
PUNK_APPS {type pathlist} PUNK_APPS {type pathlist}
PUNK_CONFIG {type string} PUNK_CONFIG {type string}
PUNK_CONFIGSET {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_SCRIPTLIB {type string}
PUNK_AUTO_EXEC_MECHANISM {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"} PUNK_AUTO_NOEXEC {type string default 0 help "set 1 to set Tcl's ::auto_noexec true.\nStops 'unknown' from running external programs"}

1
src/modules/punk/config-buildversion.txt

@ -1,6 +1,7 @@
0.2.0 0.2.0
#First line must be a semantic version number #First line must be a semantic version number
#all other lines are ignored. #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 - 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.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) #0.1 - prior manually-versioned baseline (filename config-0.1.tm, no buildversion.txt)

Loading…
Cancel
Save