diff --git a/src/buildsuites/suite_tcl90/build905.zig b/src/buildsuites/suite_tcl90/build905.zig index 75a4ed29..5eb614a9 100644 --- a/src/buildsuites/suite_tcl90/build905.zig +++ b/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) - var family_tree_base: [2]std.Build.LazyPath = undefined; - for (0..2) |ti| { //0 = core payload (plain+punk), 1 = bi payload + //default 2: r1 of every (9.0.5, variant) pair was PUBLISHED to punkbin + //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 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, 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(); //core script library at the boot position (tzdata/encoding ride //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 _ = 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) 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.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(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.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.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_installed: [family_kits.len][]const u8 = undefined; for (family_kits, 0..) |fk, ki| { @@ -2419,9 +2542,9 @@ pub fn build(b: *std.Build) !void { wrap.addArg("-outfile"); const wrapped = wrap.addOutputFileArg(fk.working_name); wrap.addArg("-indir"); - wrap.addDirectoryArg(family_tree_base[fk.tree]); + wrap.addDirectoryArg(family_tree_base[ki]); wrap.addArg("-strip"); - wrap.addDirectoryArg(family_tree_base[fk.tree]); + wrap.addDirectoryArg(family_tree_base[ki]); wrap.addArg("-infile"); wrap.addArtifactArg(fk.prefix_exe); 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. //Depends on the checks: only verified kits get artifact records. //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 - //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 family artifact names (kit-family-artifacts; default 2 - r1 published 2026-07-22)") orelse 2; + //(familyrev + the schema v1 identity options and checkout uuids are + //declared above with the family staging - the embedded records need them) const family_artifacts_step = b.step("kit-family-artifacts", "emit punkbin-layout artifact copies (-r) + toml metadata + sha1sums for the verified family kits -> /family/punkbin/"); 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]}); common.scrubTclEnv(emit); emit.has_side_effects = true;