diff --git a/goals/G-101-tcl86-kit-container-strategy.md b/goals/G-101-tcl86-kit-container-strategy.md index 2ce15f64..26200803 100644 --- a/goals/G-101-tcl86-kit-container-strategy.md +++ b/goals/G-101-tcl86-kit-container-strategy.md @@ -204,6 +204,39 @@ becomes this container's payload. (win32-x86_64 + win32-ix86), so the ix86 arm of whatever is decided here is buildable from day one. +## Progress + +Increment 1 landed 2026-08-02: the G-153 ready-to-land records are CONSUMED - +suite_tcl86 now builds the mk4 candidate's loadable shape from the pinned fork. + +- sources.config `source metakit git ... 6a92b479... metakit` + build.zig.zon + `.metakit` record (both flows carry the same fork identity; the zon url+hash + pair re-verified live at landing - `zig fetch` under the pinned zig prints + exactly the recorded hash). +- build_mk4tcl86/build_mk4tcl86.zig (new module, build_tclvfs86 conventions): + the storage engine as its own static lib (upstream CMake q4_UNIV source set, + -fno-exceptions -fno-rtti) + Mk4tcl.dll (mk4tcl.cpp + bundled mk4too.cpp, + USE_TCL_STUBS + BUILD_Mk4tcl) linked against the suite's libtclstub86; + package version parsed from the staged mk4tcl.cpp Tcl_PkgProvide (never + hardcoded); installs lib/mk4tcl2.4.9.8/{Mk4tcl.dll,pkgIndex.tcl} onto the + default install step. Fork facts carried in the module header; the fork's + own build.zig mk4tcl step is the reference recipe (keep the two in step). +- build86.zig wiring: build-mk4tcl step, pkg_smoke gains Mk4tcl (both shells), + bootstrapMode source_dirs + recipe_items rows; suite.tcl required-source + list + staged recipe item list. +- VERIFIED (dev flow, 2026-08-02): `suite.tcl build -steps {install + install-libraries smoke}` cloned the fork from the gitea record and checked + out the pin, built and installed the dll, and pkg_smoke reports + Mk4tcl:2.4.9.8 in BOTH shells (tclsh86t dynamic AND tclsh86ts static) + alongside Thread/vfs/registry/dde/Tk. + +Remaining for acceptance: the candidate-container assessments recorded against +the kit expectations (mk4 / cookfs / zipvfs-over-tclvfs, with the +zipfs-backport arm's bearing); one strategy demonstrated end-to-end (kit +wrapped from the suite-built runtime boots with no installed Tcl and runs a +script from inside its vfs, wrapping a SEPARATE copy per the self-mount +rider); decision + rationale + follow-on-to-parity record in this file. + ## Reference material (TEMP_REFERENCE, machine-local) Recorded 2026-07-26 from an agent survey. CAVEAT: TEMP_REFERENCE is ignored by diff --git a/src/buildsuites/suite_tcl86/README.md b/src/buildsuites/suite_tcl86/README.md index 1044fc5b..5674dd78 100644 --- a/src/buildsuites/suite_tcl86/README.md +++ b/src/buildsuites/suite_tcl86/README.md @@ -85,6 +85,18 @@ ACTIVE - see `goals/G-100-suite-tcl86-tk-tcllib.md` for the progress ledger. (suite.tcl `-zigargs {-Dlibrev=N}`; default 1). Publication to the real punkbin repo remains a deliberate user step. +## Deliverables (G-101 kit-container work) + +- Mk4tcl **2.4.9.8** (`lib/mk4tcl2.4.9.8/Mk4tcl.dll` + generated pkgIndex.tcl, + mk4too bundled - it is a hard link dependency and the oomk consumer needs it): + the mk4 container-candidate engine + `mk::*` binding, built from the + punkshell-maintained metakit_punk fork by `build_mk4tcl86/` (engine + -fno-exceptions/-fno-rtti, binding a stubs consumer against + `lib/libtclstub86.a`). `package require Mk4tcl` proven in BOTH shells by the + `smoke` step's pkg_smoke. Kit boot machinery (kitInit/boot.tcl/setupvfs + analogs) is deliberately NOT here yet - that is G-101's open investigation + (see `goals/G-101-tcl86-kit-container-strategy.md`). + ## Sources (both flows carry the same identity) - Dev flow: `sources.config` (live refs; `suite.tcl build` stages checkouts @@ -118,6 +130,16 @@ Selections (2026-07-26, G-100 dependency set): (`lib/critcl/critcl_c/tcl8.6/` here), which is why one config file serves both generations. +Selections (2026-08-02, G-101 kit-container work): + +- metakit: the punkshell-maintained fork (gitea `jn/metakit`; project name + metakit_punk, pooryorick 2.4.9.8 porting baseline) pinned by commit + 6a92b479 - the G-153-verified tree (standalone build, regress, cross-arch + interchange and dll smokes all ran at exactly that commit; see + `goals/archive/G-153-metakit-punk-zig-port.md`). No tags upstream yet, so + commit-pinned like punkzip; both flows carry the same identity (the zon + hash was re-verified live when the records landed). + ## 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 diff --git a/src/buildsuites/suite_tcl86/build.zig.zon b/src/buildsuites/suite_tcl86/build.zig.zon index b429da6a..e6c3fa26 100644 --- a/src/buildsuites/suite_tcl86/build.zig.zon +++ b/src/buildsuites/suite_tcl86/build.zig.zon @@ -81,6 +81,15 @@ .hash = "punkzip-2.3.1-PjFoDUNlgwCyYfz8k0-SNGxaXsh53YWTXv5f9JRRXQK8", .lazy = true, }, + //metakit_punk fork commit 6a92b479 (no tags yet) - the G-153-verified + //tree: mk4 engine + Mk4tcl/mk4too binding for the G-101 kit-container + //work. Hash computed with the pinned zig 0.16.0 (2026-08-02, recorded + //at the G-153 pin capture - goals/archive/G-153-metakit-punk-zig-port.md). + .metakit = .{ + .url = "https://www.gitea1.intx.com.au/jn/metakit/archive/6a92b47955c986e8d04db0a3f9555710482bc56d.tar.gz", + .hash = "metakit_punk-0.1.0-fjiV29tTbQAB6kfuzK76YFyu7-HYnwGJKwNlMFyBTECZ", + .lazy = true, + }, }, .paths = .{""}, } diff --git a/src/buildsuites/suite_tcl86/build86.zig b/src/buildsuites/suite_tcl86/build86.zig index f93d636c..85e93573 100644 --- a/src/buildsuites/suite_tcl86/build86.zig +++ b/src/buildsuites/suite_tcl86/build86.zig @@ -57,6 +57,7 @@ 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 mk4tcl86 = @import("build_mk4tcl86/build_mk4tcl86.zig"); const tcl_dot_version = "8.6"; const tcl_nodot_version = "86"; @@ -617,6 +618,13 @@ pub fn build(b: *std.Build) !void { build_tclvfs_step.dependOn(&tclvfs_compile.step); try tclvfs86.install_tclvfs_package("../tclvfs", b, tclvfs_compile); + //-- Mk4tcl (G-101): the metakit_punk fork's engine + mk::* binding as a + //suite battery - the mk4 container candidate's loadable shape (lib/mk4tcl) + const mk4tcl_compile = try mk4tcl86.build_mk4tcl86(tcl_source_folder, "../metakit", b, target, optimize, finalstublib); + const build_mk4tcl_step = b.step("build-mk4tcl", "build the Mk4tcl loadable extension (metakit_punk fork; includes mk4too)"); + build_mk4tcl_step.dependOn(&mk4tcl_compile.step); + try mk4tcl86.install_mk4tcl_package("../metakit", b, mk4tcl_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); @@ -696,7 +704,7 @@ pub fn build(b: *std.Build) !void { pkgs.has_side_effects = true; pkgs.setName(b.fmt("pkg_smoke {s}", .{sr.label})); pkgs.addFileArg(b.path("tools/pkg_smoke.tcl")); - pkgs.addArgs(&.{ "-gui", "1", "Thread", "vfs", "registry", "dde", "Tk" }); + pkgs.addArgs(&.{ "-gui", "1", "Thread", "vfs", "registry", "dde", "Tk", "Mk4tcl" }); pkgs.step.dependOn(b.getInstallStep()); pkgs.step.dependOn(install_libraries); smoke_step.dependOn(&pkgs.step); @@ -1119,6 +1127,8 @@ fn bootstrapMode(b: *std.Build) !void { .{ "critcl", "critcl" }, //G-138 library-artifacts .{ "punkzip", "punkzip" }, + //G-101 kit-container work: the metakit_punk fork (engine + Mk4tcl binding) + .{ "metakit", "metakit" }, }; var deps_pending = false; for (source_dirs) |sd| { @@ -1157,6 +1167,7 @@ fn bootstrapMode(b: *std.Build) !void { "build_tclthread86", "build_tclvfs86", "build_tk86", + "build_mk4tcl86", "critcl_zig.config", "expected_test_failures.txt", "expected_test_failures_thread.txt", diff --git a/src/buildsuites/suite_tcl86/build_mk4tcl86/build_mk4tcl86.zig b/src/buildsuites/suite_tcl86/build_mk4tcl86/build_mk4tcl86.zig new file mode 100644 index 00000000..9ce67ca8 --- /dev/null +++ b/src/buildsuites/suite_tcl86/build_mk4tcl86/build_mk4tcl86.zig @@ -0,0 +1,125 @@ +const std = @import("std"); +const common = @import("../build_common.zig"); + +//build_mk4tcl86 - Mk4tcl (metakit_punk fork) loadable extension for the 8.6 +//suite (G-101). The metakit port itself lives in the fork-as-upstream repo +//(gitea jn/metakit; G-153 - built, regress-tested and smoke-verified standalone +//under the pinned zig, win32-x86_64 + win32-ix86). This module re-expresses the +//fork's own 'mk4tcl' build step in suite shape (build_tclvfs86 conventions: +//staged source tree beside the recipe, the suite's stub lib, install hooked onto +//the default step) so the suite's kit-container work consumes suite-built bytes. +// +//Fork facts carried over (decision records: metakit fork src/AGENTS.md + +//build.zig - keep the two recipes in step): +// - engine sources = upstream CMake metakit_SOURCES (q4_UNIV default set: +// univ.cpp in, std.cpp out), compiled -fno-exceptions -fno-rtti (the ported +// core is exception- and RTTI-free); mk4tcl.cpp keeps exceptions ON (real +// try/catch). +// - mk4too.cpp is bundled: a hard link dependency of mk4tcl.cpp (mk::view +// open/new construct MkView unguarded) and the oomk consumer needs it - +// matching both upstream builds (CMake and KitCreator). +// - -fno-sanitize=undefined/-fno-sanitize-trap=undefined everywhere: zig cc's +// default trapping UBSan fires on 2007-era formal UB (suite tcl/tk/zlib +// precedent; first site derived.cpp:308). +// - BUILD_Mk4tcl gates TCL_STORAGE_CLASS = DLLEXPORT in mk4tcl.h (TEA form); +// USE_TCL_STUBS for the stubs contract (proven cross-patchlevel and +// cross-CRT in the G-153 smokes). +// - dll name stays Mk4tcl.dll (fork/upstream-recognizable, no TEA-nmake +// vfs142t-style renaming): unlike tclvfs there is no script loading +// @PKG_LIB_FILE8@ by guessed init name - the generated pkgIndex passes the +// init name explicitly ('load ... Mk4tcl'; load-name guessing stops at +// digits, the historical Mk_Init quirk). + +//upstream src/CMakeLists.txt metakit_SOURCES (q4_UNIV default set) - same list +//as the fork build.zig core_sources +const core_sources = [_][]const u8{ + "column.cpp", "custom.cpp", "derived.cpp", "field.cpp", "fileio.cpp", + "format.cpp", "handler.cpp", "persist.cpp", "remap.cpp", "store.cpp", + "string.cpp", "table.cpp", "univ.cpp", "view.cpp", "viewx.cpp", +}; + +const engine_flags = [_][]const u8{ + "-fno-exceptions", + "-fno-rtti", + "-fno-sanitize=undefined", + "-fno-sanitize-trap=undefined", +}; + +const binding_flags = [_][]const u8{ + "-DUSE_TCL_STUBS=1", + "-DBUILD_Mk4tcl", + "-fno-sanitize=undefined", + "-fno-sanitize-trap=undefined", +}; + +//package version fact from the staged fork tree (never hardcoded): the +//Tcl_PkgProvide call in mk4tcl.cpp is the authority for what the built dll +//actually provides ('package provide stays 2.4.9.8' is the fork's recorded +//version policy - the upstream metakit version, not a fork-local number). +pub fn packageVersion(b: *std.Build, comptime subdir: []const u8) []const u8 { + const src = common.readSourceFile(b, subdir ++ "/src/tcl/mk4tcl.cpp"); + const marker = "Tcl_PkgProvide(interp, \"Mk4tcl\", \""; + const at = std.mem.indexOf(u8, src, marker) orelse @panic("Tcl_PkgProvide(\"Mk4tcl\", ...) not found in mk4tcl.cpp (staged metakit tree mismatch?)"); + const rest = src[at + marker.len ..]; + const end = std.mem.indexOfScalar(u8, rest, '"') orelse @panic("unterminated Tcl_PkgProvide version string in mk4tcl.cpp"); + return b.dupe(rest[0..end]); +} + +//install the package dir (lib/mk4tcl, suite convention beside +//lib/thread2.8.13 and lib/vfs1.4.2): the dll + a generated pkgIndex.tcl +pub fn install_mk4tcl_package(comptime subdir: []const u8, b: *std.Build, mk4tcl_compile: *std.Build.Step.Compile) !void { + const ver = packageVersion(b, subdir); + const pkgsub = b.fmt("lib/mk4tcl{s}", .{ver}); + const dll_inst = b.addInstallFileWithDir(mk4tcl_compile.getEmittedBin(), .prefix, b.fmt("{s}/Mk4tcl.dll", .{pkgsub})); + b.getInstallStep().dependOn(&dll_inst.step); + const wf = b.addWriteFiles(); + const pkgindex = wf.add("pkgIndex.tcl", b.fmt( + \\package ifneeded Mk4tcl {s} [list load [file join $dir Mk4tcl.dll] Mk4tcl] + \\ + , .{ver})); + const inst_pkgindex = b.addInstallFileWithDir(pkgindex, .prefix, b.fmt("{s}/pkgIndex.tcl", .{pkgsub})); + b.getInstallStep().dependOn(&inst_pkgindex.step); +} + +pub fn build_mk4tcl86(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 { + //the Tcl-agnostic storage engine as its own static lib so the + //exceptions-off/rtti-off flag set stays separate from the binding's + const engine = b.addLibrary(.{ + .linkage = .static, + .name = "mk4", + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), + }); + engine.root_module.addIncludePath(b.path(subdir ++ "/src/include")); + inline for (core_sources) |nm| { + engine.root_module.addCSourceFile(.{ + .file = b.path(subdir ++ "/src/core/" ++ nm), + .flags = &engine_flags, + }); + } + engine.root_module.link_libcpp = true; + + const mk4tcl = b.addLibrary(.{ + .linkage = .dynamic, + .name = "Mk4tcl", + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), + }); + mk4tcl.root_module.addIncludePath(b.path(subdir ++ "/src/include")); + mk4tcl.root_module.addIncludePath(b.path(subdir ++ "/src/core")); //univ.h (fork-edit note in the fork's mk4tcl.h) + mk4tcl.root_module.addIncludePath(b.path(tcldir ++ "/generic")); + inline for (.{ "mk4tcl.cpp", "mk4too.cpp" }) |nm| { + mk4tcl.root_module.addCSourceFile(.{ + .file = b.path(subdir ++ "/src/tcl/" ++ nm), + .flags = &binding_flags, + }); + } + mk4tcl.root_module.linkLibrary(engine); + mk4tcl.root_module.linkLibrary(stublib); + mk4tcl.root_module.link_libcpp = true; + return mk4tcl; +} diff --git a/src/buildsuites/suite_tcl86/sources.config b/src/buildsuites/suite_tcl86/sources.config index e60ba4d7..6e95cb90 100644 --- a/src/buildsuites/suite_tcl86/sources.config +++ b/src/buildsuites/suite_tcl86/sources.config @@ -42,6 +42,7 @@ 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 Mk4tcl 2.4.9.8 (Mk4tcl.dll, metakit_punk fork, mk4too bundled) loadable in both shells - the mk4 container engine for the G-101 kit work 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 punkbin lib-tier library artifacts (G-138): tcllib/tcllibc -tcl8-r zips with embedded schema-v2 records - out/library/punkbin/lib 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 @@ -64,3 +65,9 @@ source critcl git https://github.com/andreas-kupries/critcl 3.3.1 critc # for the library-artifacts step. Generation-neutral (same pin as suite_tcl90); # pinned by commit (v2.3.1; the repo carries no tags). Fetched lazily like critcl. source punkzip git https://www.gitea1.intx.com.au/jn/punkzip 0882f0373eb2c983142e713c814b91aa82cfcea7 punkzip + +# G-101: metakit_punk (punkshell-maintained fork of Metakit, pooryorick 2.4.9.8 +# baseline) - the mk4 container engine + Mk4tcl/mk4too binding for the 8.6 kit +# work. Pinned by commit like punkzip (the fork carries no tags yet); the pin is +# the G-153-verified tree (build/regress/interchange/dll-smoke all run at it). +source metakit git https://www.gitea1.intx.com.au/jn/metakit 6a92b47955c986e8d04db0a3f9555710482bc56d metakit diff --git a/src/buildsuites/suite_tcl86/suite.tcl b/src/buildsuites/suite_tcl86/suite.tcl index c8c5d758..c53ad86d 100644 --- a/src/buildsuites/suite_tcl86/suite.tcl +++ b/src/buildsuites/suite_tcl86/suite.tcl @@ -310,13 +310,13 @@ proc git_source {name url ref dir} { #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 build_tk86 critcl_zig.config 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 build_mk4tcl86 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 tk tklib tcllib critcl punkzip} { +foreach required {tcl tclthread tclvfs tk tklib tcllib critcl punkzip metakit} { if {![dict exists $sources $required]} { fail "sources.config: missing required source record '$required'" }