Browse Source

G-117 staging: per-member embedded artifact record (schema v1) in the kit-family trees

build905.zig: payload trees become per-member (shared content + the member's
base/punkbin-artifact.toml embedded record - namespaced for G-025 coexistence,
survives make.tcl kit wrapping). Record carries schema=1, artifact identity
(name/-rN/variant/working_name/target), build_id (uuid-shaped sha1 digest of
stable identity inputs - deterministic, zig-cache-friendly; correlation key,
not integrity), origin (-Doriginurl, default punkbin repo url), packager
(-Dpackager > env PUNKBIN_PACKAGER > git identity > unrecorded), project/
project_url (-Dprojecturl, default unrecorded), license summary,
build_host_platform, [runtime] piperepl facts + batteries, [provenance]
checkout uuids - with '#' comments on the non-obvious fields. sha1/size/built
stay sidecar-only (embed-then-hash, no circularity). familyrev + checkout-uuid
declarations hoisted to the staging section (records need them); wrap loop
indexes per-member trees. Configure-compiled clean under pinned zig 0.16.0
(environ_map / StdIo lowercase API fixes).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
05eebe1750
  1. 172
      src/buildsuites/suite_tcl90/build905.zig

172
src/buildsuites/suite_tcl90/build905.zig

@ -2368,8 +2368,141 @@ pub fn build(b: *std.Build) !void {
\\ \\
; ;
const tkb = tk_build.?; //windows target (checked just above) const tkb = tk_build.?; //windows target (checked just above)
var family_tree_base: [2]std.Build.LazyPath = undefined; //default 2: r1 of every (9.0.5, variant) pair was PUBLISHED to punkbin
for (0..2) |ti| { //0 = core payload (plain+punk), 1 = bi payload //2026-07-22 and is immutable - subsequent assemblies (first content
//change: tclvfs forTcl9/1.5.0) emit as r2 until r2 is itself published
//and superseded. Bump this default whenever the current rev publishes.
const familyrev = b.option(u32, "familyrev", "assembly revision N for the -r<N> family artifact names (kit-family-artifacts; default 2 - r1 published 2026-07-22)") orelse 2;
//G-117 schema v1 identity options. origin = canonical artifact repo the
//artifacts are BUILT FOR (not necessarily where they end up hosted -
//mirrors preserve it). packager = declared identity, resolution chain
//-Dpackager > env PUNKBIN_PACKAGER > building checkout's git identity >
//"unrecorded" (declarative, not proof - signing is the verification layer).
const originurl = b.option([]const u8, "originurl", "canonical artifact repo url the family artifacts are built FOR ('origin' field; default punkbin)") orelse "https://www.gitea1.intx.com.au/jn/punkbin";
const projecturl = b.option([]const u8, "projecturl", "project url recorded in the artifact records ('project_url' field; default unrecorded)") orelse "unrecorded";
const packager = blk: {
if (b.option([]const u8, "packager", "declared packager identity for the artifact records (-Dpackager > env PUNKBIN_PACKAGER > git identity > unrecorded)")) |p| break :blk p;
if (b.graph.environ_map.get("PUNKBIN_PACKAGER")) |p| {
if (p.len != 0) break :blk p;
}
var gc: u8 = undefined;
const gname: ?[]const u8 = b.runAllowFail(&.{ "git", "config", "user.name" }, &gc, .ignore) catch null;
if (gname != null and gc == 0) {
const nm = std.mem.trim(u8, gname.?, " \t\r\n");
if (nm.len != 0) {
const gmail: ?[]const u8 = b.runAllowFail(&.{ "git", "config", "user.email" }, &gc, .ignore) catch null;
if (gmail != null and gc == 0) {
const em = std.mem.trim(u8, gmail.?, " \t\r\n");
if (em.len != 0) break :blk b.fmt("{s} <{s}>", .{ nm, em });
}
break :blk nm;
}
}
break :blk "unrecorded";
};
const FamilyKit = struct {
variant: []const u8,
working_name: []const u8,
prefix_exe: *std.Build.Step.Compile,
tree: usize,
};
const family_kits = [_]FamilyKit{
.{ .variant = "plain", .working_name = b.fmt("tclsh{s}.exe", .{tcl_h_patchlevel}), .prefix_exe = tclsh_exe, .tree = 0 },
.{ .variant = "punk", .working_name = b.fmt("tclsh{s}-punk.exe", .{tcl_h_patchlevel}), .prefix_exe = tclsh_pr_exe, .tree = 0 },
.{ .variant = "punk-bi", .working_name = b.fmt("tclsh{s}-punk-bi.exe", .{tcl_h_patchlevel}), .prefix_exe = tclsh_pr_exe, .tree = 1 },
};
//source checkout uuids (also consumed by kit-family-artifacts below)
const uuid_tcl = common.manifestUuid(b, tcl_source_folder);
const uuid_thread = common.manifestUuid(b, "../tclthread");
const uuid_tclvfs = common.manifestUuid(b, "../tclvfs");
const uuid_tk = common.manifestUuid(b, "../tk9");
const uuid_tcllib = common.manifestUuid(b, "../tcllib");
const uuid_tklib = common.manifestUuid(b, "../tklib");
//G-117: per-member EMBEDDED artifact record (schema v1), staged into each
//member's payload tree as base/punkbin-artifact.toml (namespaced so a
//future G-025 kit-stamp file can coexist; the record survives make.tcl
//kit wrapping, which overlays without clearing). Finished-binary facts
//(sha1, size, built) stay SIDECAR-ONLY: embed-then-hash ordering, no
//circularity - the sidecar + sha1sums.txt remain the integrity authority.
//build_id is a uuid-shaped sha1 digest over stable identity inputs
//(artifact name incl -r<N>, toolchain/optimize, patchlevel, the member's
//source-checkout uuids) - deterministic so the zig cache holds across
//configures; a correlation key, not an integrity key.
var family_records: [family_kits.len][]const u8 = undefined;
for (family_kits, 0..) |fk, ki| {
const kbi = fk.tree == 1;
const wroot = fk.working_name[0 .. fk.working_name.len - 4]; //strip ".exe" (windows branch)
const artifact_name = b.fmt("{s}-r{d}.exe", .{ wroot, familyrev });
const prov_seed = if (kbi)
b.fmt("tcl {s} thread {s} tclvfs {s} tcllib {s} tk {s} tklib {s}", .{ uuid_tcl, uuid_thread, uuid_tclvfs, uuid_tcllib, uuid_tk, uuid_tklib })
else
b.fmt("tcl {s} thread {s} tclvfs {s} tcllib {s}", .{ uuid_tcl, uuid_thread, uuid_tclvfs, uuid_tcllib });
var idigest: [20]u8 = undefined;
std.crypto.hash.Sha1.hash(b.fmt("punkbin-artifact|{s}|zig {s}|{s}|tcl{s}|{s}", .{ artifact_name, builtin.zig_version_string, @tagName(optimize), tcl_h_patchlevel, prov_seed }), &idigest, .{});
const ihex = std.fmt.bytesToHex(idigest, .lower);
const build_id = b.fmt("{s}-{s}-{s}-{s}-{s}", .{ ihex[0..8], ihex[8..12], ihex[12..16], ihex[16..20], ihex[20..32] });
const batteries = if (kbi)
b.fmt("[\"Thread {s}\", \"vfs {s}\", \"tcllib {s}\", \"tcllibc {s}\", \"Tk {s}\", \"tklib {s}\"]", .{ thread_build.version, tclvfs_info.dotversion, tcllib_ver, tcllib_ver, tkb.patchlevel, tklib_ver })
else
b.fmt("[\"Thread {s}\", \"vfs {s}\", \"tcllib {s}\", \"tcllibc {s}\"]", .{ thread_build.version, tclvfs_info.dotversion, tcllib_ver, tcllib_ver });
const piperepl_block: []const u8 = if (std.mem.eql(u8, fk.variant, "plain"))
"piperepl = false"
else
"piperepl = true\npiperepl_default = \"on\"\npiperepl_opt_out = \"TCLSH_PIPEREPL=0\"";
const prov_lines = if (kbi)
b.fmt("tcl_checkout = \"{s}\"\nthread_checkout = \"{s}\"\ntclvfs_checkout = \"{s}\"\ntk_checkout = \"{s}\"\ntcllib_checkout = \"{s}\"\ntklib_checkout = \"{s}\"", .{ uuid_tcl, uuid_thread, uuid_tclvfs, uuid_tk, uuid_tcllib, uuid_tklib })
else
b.fmt("tcl_checkout = \"{s}\"\nthread_checkout = \"{s}\"\ntclvfs_checkout = \"{s}\"\ntcllib_checkout = \"{s}\"", .{ uuid_tcl, uuid_thread, uuid_tclvfs, uuid_tcllib });
family_records[ki] = b.fmt(
\\#punkshell runtime artifact metadata - EMBEDDED copy (G-117 schema v1),
\\#written at kit-family staging. Finished-binary facts (sha1, size, built)
\\#live only in the sidecar toml + sha1sums.txt, which remain the integrity
\\#authority; this copy makes a stray or renamed runtime self-describing.
\\schema = 1
\\
\\[artifact]
\\name = "{s}"
\\class = "runtime"
\\variant = "{s}"
\\working_name = "{s}"
\\revision = {d}
\\target = "win32-x86_64"
\\#build_id: offline correlation key re-joining a renamed copy to its record;
\\#identical in embedded and sidecar copies. Uuid-shaped digest of stable
\\#identity inputs (deterministic) - not an integrity key.
\\build_id = "{s}"
\\#origin: canonical artifact repo this artifact was BUILT FOR - not
\\#necessarily where it is hosted; mirrors preserve it.
\\origin = "{s}"
\\#packager: declared identity, not proof - signing (minisign sidecars)
\\#is the verification layer.
\\packager = "{s}"
\\project = "punkshell"
\\project_url = "{s}"
\\#license: summary for the distributed artifact; component license texts
\\#ride inside the attached image (tcl_library/license.terms etc).
\\license = "TCL"
\\build_host_platform = "win32-x86_64"
\\
\\[runtime]
\\tcl_patchlevel = "{s}"
\\{s}
\\attached_batteries = {s}
\\
\\[provenance]
\\suite = "suite_tcl90"
\\toolchain = "zig {s}"
\\optimize = "{s}"
\\{s}
\\
, .{ artifact_name, fk.variant, fk.working_name, familyrev, build_id, originurl, packager, projecturl, tcl_h_patchlevel, piperepl_block, batteries, builtin.zig_version_string, @tagName(optimize), prov_lines });
}
var family_tree_base: [family_kits.len]std.Build.LazyPath = undefined;
for (family_kits, 0..) |fk, ki| { //per-member payload tree: shared content + the member's embedded record
const famwf = b.addWriteFiles(); const famwf = b.addWriteFiles();
//core script library at the boot position (tzdata/encoding ride //core script library at the boot position (tzdata/encoding ride
//inside the source library tree), dde/registry dlls inside //inside the source library tree), dde/registry dlls inside
@ -2383,32 +2516,22 @@ pub fn build(b: *std.Build) !void {
} }
//package-search hook for the lib/ tree //package-search hook for the lib/ tree
_ = famwf.add("base/lib/pkgIndex.tcl", family_libext_pkgindex); _ = famwf.add("base/lib/pkgIndex.tcl", family_libext_pkgindex);
//G-117 embedded artifact record (per-member; see the record block above)
_ = famwf.add("base/punkbin-artifact.toml", family_records[ki]);
//batteries (installed shape) //batteries (installed shape)
try tclvfs_shared.familyadd_tclvfs_package("../tclvfs", b, famwf, b.fmt("base/lib/vfs{s}", .{tclvfs_info.dotversion}), tclvfs_compile); try tclvfs_shared.familyadd_tclvfs_package("../tclvfs", b, famwf, b.fmt("base/lib/vfs{s}", .{tclvfs_info.dotversion}), tclvfs_compile);
_ = famwf.addCopyFile(thread_build.pkgidx, b.fmt("base/lib/thread{s}/pkgIndex.tcl", .{thread_build.version})); _ = famwf.addCopyFile(thread_build.pkgidx, b.fmt("base/lib/thread{s}/pkgIndex.tcl", .{thread_build.version}));
_ = famwf.addCopyFile(thread_build.lib.getEmittedBin(), b.fmt("base/bin/{s}", .{thread_build.dll_file})); _ = famwf.addCopyFile(thread_build.lib.getEmittedBin(), b.fmt("base/bin/{s}", .{thread_build.dll_file}));
_ = famwf.addCopyDirectory(tcllib_out, b.fmt("base/lib/tcllib{s}", .{tcllib_ver}), .{}); _ = famwf.addCopyDirectory(tcllib_out, b.fmt("base/lib/tcllib{s}", .{tcllib_ver}), .{});
_ = famwf.addCopyDirectory(critcl_libout, "base/lib", .{}); //carries tcllibc/ _ = famwf.addCopyDirectory(critcl_libout, "base/lib", .{}); //carries tcllibc/
if (ti == 1) { if (fk.tree == 1) {
_ = famwf.addCopyDirectory(b.path("../tk9/library"), b.fmt("base/lib/{s}", .{tkb.libdir}), .{}); _ = famwf.addCopyDirectory(b.path("../tk9/library"), b.fmt("base/lib/{s}", .{tkb.libdir}), .{});
_ = famwf.addCopyFile(tkb.pkgidx, b.fmt("base/lib/{s}/pkgIndex.tcl", .{tkb.libdir})); _ = famwf.addCopyFile(tkb.pkgidx, b.fmt("base/lib/{s}/pkgIndex.tcl", .{tkb.libdir}));
_ = famwf.addCopyFile(tkb.lib.getEmittedBin(), b.fmt("base/bin/{s}", .{tkb.dll_file})); _ = famwf.addCopyFile(tkb.lib.getEmittedBin(), b.fmt("base/bin/{s}", .{tkb.dll_file}));
_ = famwf.addCopyDirectory(tklib_out, b.fmt("base/lib/tklib{s}", .{tklib_ver}), .{}); _ = famwf.addCopyDirectory(tklib_out, b.fmt("base/lib/tklib{s}", .{tklib_ver}), .{});
} }
family_tree_base[ti] = fam_tcl_library.dirname(); family_tree_base[ki] = fam_tcl_library.dirname();
} }
const FamilyKit = struct {
variant: []const u8,
working_name: []const u8,
prefix_exe: *std.Build.Step.Compile,
tree: usize,
};
const family_kits = [_]FamilyKit{
.{ .variant = "plain", .working_name = b.fmt("tclsh{s}.exe", .{tcl_h_patchlevel}), .prefix_exe = tclsh_exe, .tree = 0 },
.{ .variant = "punk", .working_name = b.fmt("tclsh{s}-punk.exe", .{tcl_h_patchlevel}), .prefix_exe = tclsh_pr_exe, .tree = 0 },
.{ .variant = "punk-bi", .working_name = b.fmt("tclsh{s}-punk-bi.exe", .{tcl_h_patchlevel}), .prefix_exe = tclsh_pr_exe, .tree = 1 },
};
var family_checks: [family_kits.len]*std.Build.Step = undefined; var family_checks: [family_kits.len]*std.Build.Step = undefined;
var family_installed: [family_kits.len][]const u8 = undefined; var family_installed: [family_kits.len][]const u8 = undefined;
for (family_kits, 0..) |fk, ki| { for (family_kits, 0..) |fk, ki| {
@ -2419,9 +2542,9 @@ pub fn build(b: *std.Build) !void {
wrap.addArg("-outfile"); wrap.addArg("-outfile");
const wrapped = wrap.addOutputFileArg(fk.working_name); const wrapped = wrap.addOutputFileArg(fk.working_name);
wrap.addArg("-indir"); wrap.addArg("-indir");
wrap.addDirectoryArg(family_tree_base[fk.tree]); wrap.addDirectoryArg(family_tree_base[ki]);
wrap.addArg("-strip"); wrap.addArg("-strip");
wrap.addDirectoryArg(family_tree_base[fk.tree]); wrap.addDirectoryArg(family_tree_base[ki]);
wrap.addArg("-infile"); wrap.addArg("-infile");
wrap.addArtifactArg(fk.prefix_exe); wrap.addArtifactArg(fk.prefix_exe);
const inst = b.addInstallFileWithDir(wrapped, .prefix, b.fmt("family/{s}", .{fk.working_name})); const inst = b.addInstallFileWithDir(wrapped, .prefix, b.fmt("family/{s}", .{fk.working_name}));
@ -2452,19 +2575,10 @@ pub fn build(b: *std.Build) !void {
//family runtime executes real tooling from its attached batteries. //family runtime executes real tooling from its attached batteries.
//Depends on the checks: only verified kits get artifact records. //Depends on the checks: only verified kits get artifact records.
//Publication to the real punkbin repo stays a deliberate user step. //Publication to the real punkbin repo stays a deliberate user step.
//default 2: r1 of every (9.0.5, variant) pair was PUBLISHED to punkbin //(familyrev + the schema v1 identity options and checkout uuids are
//2026-07-22 and is immutable - subsequent assemblies (first content //declared above with the family staging - the embedded records need them)
//change: tclvfs forTcl9/1.5.0) emit as r2 until r2 is itself published
//and superseded. Bump this default whenever the current rev publishes.
const familyrev = b.option(u32, "familyrev", "assembly revision N for the -r<N> family artifact names (kit-family-artifacts; default 2 - r1 published 2026-07-22)") orelse 2;
const family_artifacts_step = b.step("kit-family-artifacts", "emit punkbin-layout artifact copies (-r<N>) + toml metadata + sha1sums for the verified family kits -> <prefix>/family/punkbin/<target>"); const family_artifacts_step = b.step("kit-family-artifacts", "emit punkbin-layout artifact copies (-r<N>) + toml metadata + sha1sums for the verified family kits -> <prefix>/family/punkbin/<target>");
const artifacts_outdir = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "family", "punkbin", "win32-x86_64" }), "\\", "/"); const artifacts_outdir = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "family", "punkbin", "win32-x86_64" }), "\\", "/");
const uuid_tcl = common.manifestUuid(b, tcl_source_folder);
const uuid_thread = common.manifestUuid(b, "../tclthread");
const uuid_tclvfs = common.manifestUuid(b, "../tclvfs");
const uuid_tk = common.manifestUuid(b, "../tk9");
const uuid_tcllib = common.manifestUuid(b, "../tcllib");
const uuid_tklib = common.manifestUuid(b, "../tklib");
const emit = b.addSystemCommand(&.{family_installed[0]}); const emit = b.addSystemCommand(&.{family_installed[0]});
common.scrubTclEnv(emit); common.scrubTclEnv(emit);
emit.has_side_effects = true; emit.has_side_effects = true;

Loading…
Cancel
Save