Browse Source

G-102 M1: suite_tcl90 configure-products as zig overlays; source trees read-only

Generation of the configure-products a fresh checkout lacks moves from
suite.tcl tree-writes into the zig recipe as build-cache overlays:

- tclUuid.h / tkUuid.h / threadUuid.h: wrapfiletofile Run outputs consumed
  via overlay include dirs added FIRST per module (stale tree copies from
  pre-G-102 flows can never shadow; the LazyPath also carries the step dep).
  The unused addUpdateSourceFiles tree-write variants (build905 prepare-source,
  build_tclthread) are removed.
- tclsh.exe.manifest: configure-time substitution (tcl.h parsed by the new
  build_common.zig helpers); tclsh.rc compiled from an overlay COPY beside the
  generated manifest because rc resource references resolve rc-file-relative
  before include paths. Both tclsh exes switch to .include_paths LazyPaths.
- wish.exe.manifest: overlay dir first in tk.rc include_paths (RT_MANIFEST
  resolves through rc include paths - the arrangement the tk build already
  relied on). tk.rc itself stays tree-compiled (lives in win/rc, no stale risk).
- thread.rc: compiled from an overlay copy with the tcl.h include inserted
  (the historical checkout edit is normalized if a mutated tree is staged);
  the staged tclthread checkout's thread.rc edit has been fossil-reverted.
- build905.zig: redundant configure-time top-level library install loop
  removed (install_library_tree already covers it); dead zlib stage copy and
  the suite.tcl generation blocks removed (read-only version parsing kept for
  smokes/logging).

Verified: full staged-mode rebuild PASS (all smokes incl tklib/tcllib/tcllibc);
built binaries carry the overlay products (tcl::build-info reports
9.0.5+1a9c3b9d..., tclsh manifest version 9.0.2.905, tk dll manifest + tk
checkout uuid, thread dll uuid; thread dll's empty FileVersion matches the
pre-change binary - upstream quirk). Source checkouts stay pristine (fossil
changes empty). Core test gate: 69783 run, 12 failed - 11 baselined plus
http-4.14.2, a network-timing flake that passes on isolated rerun (528/519/9/0).

tclsh90sprzip.exe install into out/bin is currently blocked by an open user
application holding the file; the product builds identically into the zig
cache and installs on next run once released.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 5 days ago
parent
commit
84a1b22515
  1. 144
      src/buildsuites/suite_tcl90/build905.zig
  2. 61
      src/buildsuites/suite_tcl90/build_common.zig
  3. 34
      src/buildsuites/suite_tcl90/build_tclthread/build_tclthread.zig
  4. 40
      src/buildsuites/suite_tcl90/build_tk/build_tk.zig
  5. 61
      src/buildsuites/suite_tcl90/suite.tcl

144
src/buildsuites/suite_tcl90/build905.zig

@ -1,6 +1,7 @@
const std = @import("std");
const fs = std.fs;
const builtin = @import("builtin");
const common = @import("build_common.zig");
const build_tommath = @import("build_libtommath/build_libtommath.zig").build_libtommath;
const build_zlib = @import("build_zlib/build_zlib.zig").build_zlib;
@ -189,52 +190,14 @@ pub fn build(b: *std.Build) !void {
//if (target.result.os.tag != .windows) {
//}
//dependency on tclsh being on the system - review
//despite .cmd extension - should be callable by bash on unix, but as it's a polyglot script can have no shebang so will need to be explicit - review
//const uuid_cmd = b.addSystemCommand(&.{
// "wrapfiletofile.cmd",
// "-prefix",
// "#define TCL_VERSION_UUID \\",
// "-prefixnl",
// "1",
// "-input",
//});
// ================================================
// win/tclUuid.h & win/tclUuid.h are in the source tree but need to be created/updated
// The zig recommendation seems to be to make this a separate callable step rather than part of the standard build process.
// In this case it's required for the build, required to be in a specific source location - but shouldn't be checked in to the repo
// Ideally this should be run once on first build (and if source manifest.uuid changes) - and we shouldn't need to ask the user to specifically remember to call it.
// todo - something?
//var interp: []const []const u8 = undefined;
////note we determine the interp based on the machine running the build - not the target
//if (builtin.os.tag == .windows) {
// interp = &.{
// "wrapfiletofile.cmd",
// };
//} else {
// interp = &.{
// "bash", //explicitly use bash. Sometimes sh is linked to some other shell such as zsh, or dash (e.g debian,ubuntu) which don't understand substitution syntax like bash and ordinary posix sh do
// "wrapfiletofile.cmd",
// };
//}
//const uuid_cmd = b.addSystemCommand(interp);
//uuid_cmd.addArgs(&.{
// "-prefix",
// "#define TCL_VERSION_UUID \\",
// "-prefixnl",
// "1",
// "-input",
//});
////uuid_cmd.addFileArg(.{ .path = b.pathJoin(&.{ b.pathFromRoot(""), "..", "tcl" ++ tcl_nodot_version, "manifest.uuid" }) });
////uuid_cmd.addFileArg(.{ .src_path = b.path(b.pathJoin(&.{ b.pathFromRoot(""), "..", "tcl" ++ tcl_nodot_version, "manifest.uuid" })) });
////uuid_cmd.addFileArg(b.path(b.pathJoin(&.{ b.pathFromRoot(""), "..", "tcl" ++ tcl_nodot_version, "manifest.uuid" })));
//uuid_cmd.addFileArg(b.path(tcl_source_folder ++ "/manifest.uuid" ));
//uuid_cmd.addArg("-output");
//const tcluuid_file1 = uuid_cmd.addOutputFileArg("tclUuid.h");
const prepare_source = b.step("prepare-source", "patch and prep source");
//Generated configure-products (G-102): created into the build cache and consumed
//via overlay include dirs / overlay rc copies. Source trees (staged fossil
//checkouts or zon-fetched trees) are never written - suite.tcl formerly generated
//these files into the checkout (and before 2026-07-21 this recipe carried an
//addUpdateSourceFiles tree-write variant behind the unused 'prepare-source' step).
//
//tclUuid.h (tclEvent.c/tclTest.c #include it; upstream configure generates it)
const wrap_exe = b.addExecutable(.{
.name = "wrapfiletofile",
.root_module = b.createModule(.{
@ -242,10 +205,7 @@ pub fn build(b: *std.Build) !void {
.target = b.graph.host,
}),
});
//const tool_step = b.addRunArtifact(tool);
const wrap_run = b.addRunArtifact(wrap_exe);
//const wrap_install = b.addInstallArtifact(wrap_exe, .{});
wrap_run.addArgs(&.{
"-prefix",
"#define TCL_VERSION_UUID \\",
@ -256,24 +216,26 @@ pub fn build(b: *std.Build) !void {
wrap_run.addFileArg(b.path(tcl_source_folder ++ "/manifest.uuid"));
wrap_run.addArg("-output");
const tcluuid_file1 = wrap_run.addOutputFileArg("tclUuid.h");
//zig prior to 0.14 - addCopyFileToSource worked with b.addWriteFiles
//zig post 0.14 - b.addUpdateSourcFiles() is required to use addCopyFileToSource
//const wf_uuidh = b.addWriteFiles();
const wf_uuidh = b.addUpdateSourceFiles();
//var uuidh: std.Build.LazyPath = undefined;
if (target.result.os.tag == .windows) {
wf_uuidh.addCopyFileToSource(tcluuid_file1, b.pathJoin(&.{ tcl_source_folder, "win", "tclUuid.h" }));
} else {
wf_uuidh.addCopyFileToSource(tcluuid_file1, b.pathJoin(&.{ tcl_source_folder, "unix", "tclUuid.h" }));
}
wf_uuidh.step.dependOn(&wrap_run.step);
prepare_source.dependOn(&wf_uuidh.step);
//const copy_uuid = b.addInstallFileWithDir(tcluuid_file1, .prefix, b.pathJoin(&.{ "lib", "tclUuid.h.copy" }));
//copy_uuid.step.dependOn(&wf_uuidh.step);
//b.getInstallStep().dependOn(&copy_uuid.step);
//include-dir overlay: using the generator's output dir as an include path also
//gives each consuming compile its dependency on the generation step. Added FIRST
//at each consuming module so a stale tree-written tclUuid.h (pre-G-102 flows)
//can never shadow the generated one.
const tcluuid_include_dir = tcluuid_file1.dirname();
//tclsh.exe.manifest (tclsh.rc references it; upstream configure substitutes the
//.in). Generated at configure time; tclsh.rc is compiled from an overlay COPY
//in the same generated dir because rc resource references resolve
//rc-file-relative BEFORE include paths - a stale manifest beside the tree's
//tclsh.rc would otherwise win over any include-path overlay.
const tclh_content = common.readSourceFile(b, tcl_source_folder ++ "/generic/tcl.h");
const tcl_h_version = common.parseDefineString(tclh_content, "TCL_VERSION") orelse @panic("TCL_VERSION not found in tcl.h");
const tcl_h_patchlevel = common.parseDefineString(tclh_content, "TCL_PATCH_LEVEL") orelse @panic("TCL_PATCH_LEVEL not found in tcl.h");
const tcl_win_version = common.winResourceVersion(b, tcl_h_version, tcl_h_patchlevel);
std.debug.print("tcl.h: TCL_PATCH_LEVEL {s} (TCL_WIN_VERSION {s})\n", .{ tcl_h_patchlevel, tcl_win_version });
const tclsh_manifest_content = common.replaceAll(b, common.replaceAll(b, common.readSourceFile(b, tcl_source_folder ++ "/win/tclsh.exe.manifest.in"), "@TCL_WIN_VERSION@", tcl_win_version), "@MACHINE@", "AMD64");
const wf_tclshrc = b.addWriteFiles();
_ = wf_tclshrc.add("tclsh.exe.manifest", tclsh_manifest_content);
const tclshrc_overlay = wf_tclshrc.addCopyFile(b.path(tcl_source_folder ++ "/win/tclsh.rc"), "tclsh.rc");
// ================================================
var ac_flags = std.array_list.Managed([]const u8).init(b.allocator);
@ -1350,6 +1312,7 @@ pub fn build(b: *std.Build) !void {
}),
});
tcl_objs_static.root_module.addIncludePath(tcluuid_include_dir); //overlay first (G-102)
tcl_objs_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
if (target.result.os.tag == .windows) {
tcl_objs_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
@ -1524,6 +1487,7 @@ pub fn build(b: *std.Build) !void {
// tcl_slib.step.dependOn(&zlib_lib_compile.step);
//}
tcl_lib_shared.root_module.addIncludePath(tcluuid_include_dir); //overlay first (G-102)
tcl_lib_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
if (target.result.os.tag == .windows) {
tcl_lib_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
@ -1649,6 +1613,7 @@ pub fn build(b: *std.Build) !void {
// tclsh_exe.step.dependOn(&zlib_lib_compile.step);
//}
tclsh_exe.root_module.addIncludePath(tcluuid_include_dir); //overlay first (G-102)
tclsh_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
if (target.result.os.tag == .windows) {
tclsh_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
@ -1785,11 +1750,13 @@ pub fn build(b: *std.Build) !void {
tclsh_exe.root_module.link_libc = true;
//LDFLAGS_CONSOLE= -mconsole -pipe -static-libgcc -municode -Wl,--enable-auto-image-base
//rc compiled from the overlay copy (generated tclsh.exe.manifest beside it);
//tclsh.ico + tcl.h resolve via the include paths.
tclsh_exe.root_module.addWin32ResourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclsh.rc"),
.flags = &.{
"/I",
tcl_source_folder ++ "/generic",
.file = tclshrc_overlay,
.include_paths = &.{
b.path(tcl_source_folder ++ "/generic"),
b.path(tcl_source_folder ++ "/win"),
},
});
const install_tclsh = b.addInstallArtifact(tclsh_exe, .{});
@ -1815,6 +1782,7 @@ pub fn build(b: *std.Build) !void {
tclsh_pr_exe.mingw_unicode_entry_point = true;
tclsh_pr_exe.step.dependOn(&finalstublib.step);
tclsh_pr_exe.step.dependOn(&tcl_objs_static.step);
tclsh_pr_exe.root_module.addIncludePath(tcluuid_include_dir); //overlay first (G-102)
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
if (target.result.os.tag == .windows) {
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
@ -1901,10 +1869,10 @@ pub fn build(b: *std.Build) !void {
tclsh_pr_exe.step.dependOn(&tclvfs_compile.step); // review (parity with stock shell)
tclsh_pr_exe.root_module.link_libc = true;
tclsh_pr_exe.root_module.addWin32ResourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclsh.rc"),
.flags = &.{
"/I",
tcl_source_folder ++ "/generic",
.file = tclshrc_overlay,
.include_paths = &.{
b.path(tcl_source_folder ++ "/generic"),
b.path(tcl_source_folder ++ "/win"),
},
});
const install_tclsh_pr = b.addInstallArtifact(tclsh_pr_exe, .{});
@ -1942,30 +1910,10 @@ pub fn build(b: *std.Build) !void {
});
install_libraries.dependOn(&install_library_tree.step);
//Install library files - non recursive
var library_dir = try std.Io.Dir.cwd().openDir(b.graph.io, tcl_source_folder ++ "/library", .{ .iterate = true });
defer library_dir.close(b.graph.io);
const allowed_exts = [_][]const u8{
".tcl",
"",
};
var it = library_dir.iterate();
//var copy_libfile: []const u8 = undefined;
//var copy_libfile_step: *std.Build.Step = undefined;
var install_libfile: *std.Build.Step.InstallFile = undefined;
while (try it.next(b.graph.io)) |entry| {
if (entry.kind == .file) {
const ext = fs.path.extension(entry.name);
const include_file = for (allowed_exts) |e| {
if (std.mem.eql(u8, ext, e)) break true;
} else false;
if (include_file) {
//try generic_sources.append(b.pathJoin(&.{"tcl" ++ tcl_nodot_version ++ "/generic", entry.name,}));
install_libfile = b.addInstallFileWithDir(b.path(b.pathJoin(&.{ tcl_source_folder ++ "/library", entry.name })), .prefix, b.pathJoin(&.{ script_install_dir_rel, entry.name }));
install_libraries.dependOn(&install_libfile.step);
}
}
}
//(G-102: the former per-file top-level library install loop - a configure-time
//directory iteration - was removed as redundant: install_library_tree above
//already installs the whole library tree, top-level files included, to the same
//lib/tcl9.0 destination.)
//Makefile for tcl9.0b1 uses target folder <tcl_library>/opt0.4 <tcl_library>/cookiejar0.2 - but we don't really need the version as part of the folder structure
//At least some distributions of tcl9 don't include the version - which seems simpler

61
src/buildsuites/suite_tcl90/build_common.zig

@ -0,0 +1,61 @@
//build_common.zig - shared configure-time helpers for the suite recipe files (G-102).
//These run while the build graph is being CONSTRUCTED (not as build steps). Reads are
//of staged/fetched source trees; generated content is never written into a source
//tree - it goes through b.addWriteFiles() overlays into the build cache and is
//consumed via overlay include dirs / overlay rc copies.
const std = @import("std");
pub fn pathExists(b: *std.Build, path_from_root: []const u8) bool {
const abs = b.pathFromRoot(path_from_root);
std.Io.Dir.cwd().access(b.graph.io, abs, .{}) catch return false;
return true;
}
pub fn readSourceFile(b: *std.Build, path_from_root: []const u8) []u8 {
const abs = b.pathFromRoot(path_from_root);
return std.Io.Dir.cwd().readFileAlloc(b.graph.io, abs, b.allocator, .limited(8 * 1024 * 1024)) catch |err| {
std.debug.panic("suite recipe: cannot read {s}: {s}", .{ abs, @errorName(err) });
};
}
//value of a '#define <name> "<value>"' line (e.g TCL_PATCH_LEVEL from tcl.h)
pub fn parseDefineString(content: []const u8, name: []const u8) ?[]const u8 {
var it = std.mem.splitScalar(u8, content, '\n');
while (it.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t\r");
if (!std.mem.startsWith(u8, trimmed, "#")) continue;
const after_hash = std.mem.trimStart(u8, trimmed[1..], " \t");
if (!std.mem.startsWith(u8, after_hash, "define")) continue;
const after_def = std.mem.trimStart(u8, after_hash[6..], " \t");
if (!std.mem.startsWith(u8, after_def, name)) continue;
const after_name = after_def[name.len..];
if (after_name.len == 0 or (after_name[0] != ' ' and after_name[0] != '\t')) continue;
const q1 = std.mem.indexOfScalar(u8, after_name, '"') orelse continue;
const rest = after_name[q1 + 1 ..];
const q2 = std.mem.indexOfScalar(u8, rest, '"') orelse continue;
return rest[0..q2];
}
return null;
}
pub fn replaceAll(b: *std.Build, input: []const u8, needle: []const u8, replacement: []const u8) []u8 {
return std.mem.replaceOwned(u8, b.allocator, input, needle, replacement) catch @panic("OOM");
}
//TCL_WIN_VERSION / TK_WIN_VERSION as the makefiles derive them:
//<dotversion>.<releaselevel>.<patchlevel with a/b/. stripped>, releaselevel
//0=alpha 1=beta 2=final (e.g 9.0 + 9.0.5 -> 9.0.2.905)
pub fn winResourceVersion(b: *std.Build, dotversion: []const u8, patchlevel: []const u8) []u8 {
const rlevel: u8 = if (std.mem.indexOfScalar(u8, patchlevel, 'a') != null)
'0'
else if (std.mem.indexOfScalar(u8, patchlevel, 'b') != null)
'1'
else
'2';
var stripped = std.array_list.Managed(u8).init(b.allocator);
for (patchlevel) |ch| {
if (ch == 'a' or ch == 'b' or ch == '.') continue;
stripped.append(ch) catch @panic("OOM");
}
return std.fmt.allocPrint(b.allocator, "{s}.{c}.{s}", .{ dotversion, rlevel, stripped.items }) catch @panic("OOM");
}

34
src/buildsuites/suite_tcl90/build_tclthread/build_tclthread.zig

@ -1,6 +1,7 @@
const std = @import("std");
const fs = std.fs;
const builtin = @import("builtin");
const common = @import("../build_common.zig");
const mkvar_thread_zip_file = "libthread3.0.1.zip";
const mkvar_thread_vfs_root = "libthread.vfs";
@ -57,16 +58,9 @@ pub fn build_tclthread(comptime tcldir: []const u8, comptime subdir: []const u8,
//uuid_cmd.addArg("-output");
//const tcluuid_file1 = uuid_cmd.addOutputFileArg("threadUuid.h");
//const wf_uuidh = b.addWriteFiles();
const wf_uuidh = b.addUpdateSourceFiles();
if (target.result.os.tag == .windows) {
wf_uuidh.addCopyFileToSource(threaduuid_file, b.pathJoin(&.{ subdir, "win", "threadUuid.h" }));
} else {
wf_uuidh.addCopyFileToSource(threaduuid_file, b.pathJoin(&.{ subdir, "unix", "threadUuid.h" }));
}
wf_uuidh.step.dependOn(&uuidcmd_step.step);
b.getInstallStep().dependOn(&wf_uuidh.step);
//(G-102: the former addUpdateSourceFiles copy of threadUuid.h into the checkout
//was removed - the include-dir overlay below is the operative delivery and source
//trees are never written.)
const lib = b.addLibrary(.{
.linkage = .dynamic,
@ -143,13 +137,31 @@ pub fn build_tclthread(comptime tcldir: []const u8, comptime subdir: []const u8,
lib.root_module.linkLibrary(stublib);
lib.root_module.link_libc = true;
//thread.rc overlay (G-102): the version resource needs tcl.h; the recipe
//historically carried that as an edit of the checkout's thread.rc - now the rc
//is compiled from an overlay copy with the include inserted (and the historical
//relative-path edit normalized if a previously-mutated tree is staged), with
//tcl.h resolving via the include paths. Source trees are never written.
var threadrc_content: []const u8 = common.readSourceFile(b, subdir ++ "/win/thread.rc");
const legacy_edit = "#include \"../../tcl905/generic/tcl.h\"";
if (std.mem.indexOf(u8, threadrc_content, legacy_edit) != null) {
threadrc_content = common.replaceAll(b, threadrc_content, legacy_edit, "#include \"tcl.h\"");
} else if (std.mem.indexOf(u8, threadrc_content, "#include \"tcl.h\"") == null) {
threadrc_content = common.replaceAll(b, threadrc_content, "#include <winver.h>", "#include <winver.h>\n#include \"tcl.h\"");
}
const wf_threadrc = b.addWriteFiles();
const threadrc_overlay = wf_threadrc.add("thread.rc", threadrc_content);
lib.root_module.addWin32ResourceFile(.{
.file = b.path(subdir ++ "/win/thread.rc"),
.file = threadrc_overlay,
.flags = &.{
"-DPACKAGE_MAJOR=3",
"-DPACKAGE_MINOR=0",
"-DPACKAGE_VERSION=3.0b2",
},
.include_paths = &.{
b.path(tcldir ++ "/generic"),
b.path(subdir ++ "/win"),
},
}); //ignored for non-windows targets
b.installArtifact(lib);

40
src/buildsuites/suite_tcl90/build_tk/build_tk.zig

@ -1,4 +1,5 @@
const std = @import("std");
const common = @import("../build_common.zig");
//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
@ -91,6 +92,30 @@ pub fn build_tk(comptime tcldir: []const u8, comptime subdir: []const u8, b: *st
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");
//tkUuid.h overlay (G-102): tkWindow.c #includes it (upstream makefiles generate
//it into their build dirs). Generated into the build cache and consumed via an
//include-dir overlay added FIRST, so a stale tree-written copy in win/ (pre-G-102
//flows generated one there) can never shadow it. Source trees are never written.
const wrapfilecmd = b.addExecutable(.{
.name = "wrapfiletofile",
.root_module = b.createModule(.{
.root_source_file = b.path("tools/wrapfiletofile.zig"),
.target = b.graph.host,
}),
});
const uuidcmd_step = b.addRunArtifact(wrapfilecmd);
uuidcmd_step.addArgs(&.{
"-prefix",
"#define TK_VERSION_UUID \\",
"-prefixnl",
"1",
"-input",
});
uuidcmd_step.addFileArg(b.path(subdir ++ "/manifest.uuid"));
uuidcmd_step.addArg("-output");
const tkuuid_file = uuidcmd_step.addOutputFileArg("tkUuid.h");
lib.root_module.addIncludePath(tkuuid_file.dirname()); //overlay first (G-102)
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"));
@ -122,9 +147,24 @@ pub fn build_tk(comptime tcldir: []const u8, comptime subdir: []const u8, b: *st
.flags = mainw_flags.items,
});
//wish.exe.manifest overlay (G-102): tk.rc references it and RT_MANIFEST file
//references resolve through the rc include paths (the pre-G-102 arrangement
//already relied on that, finding a tree-written copy via subdir/win). Generated
//from the .in at configure time; the overlay dir is FIRST so a stale tree copy
//never shadows it. tk.rc itself stays tree-compiled: it lives in win/rc/ where
//no generated files were ever written, so rc-file-relative lookup cannot pick
//up stale state.
const tkh_content = common.readSourceFile(b, subdir ++ "/generic/tk.h");
const tk_h_version = common.parseDefineString(tkh_content, "TK_VERSION") orelse @panic("TK_VERSION not found in tk.h");
const tk_h_patchlevel = common.parseDefineString(tkh_content, "TK_PATCH_LEVEL") orelse @panic("TK_PATCH_LEVEL not found in tk.h");
const tk_win_version = common.winResourceVersion(b, tk_h_version, tk_h_patchlevel);
const wish_manifest_content = common.replaceAll(b, common.replaceAll(b, common.readSourceFile(b, subdir ++ "/win/wish.exe.manifest.in"), "@TK_WIN_VERSION@", tk_win_version), "@MACHINE@", "AMD64");
const wf_tkrc = b.addWriteFiles();
_ = wf_tkrc.add("wish.exe.manifest", wish_manifest_content);
lib.root_module.addWin32ResourceFile(.{
.file = b.path(subdir ++ "/win/rc/tk.rc"),
.include_paths = &.{
wf_tkrc.getDirectory(),
b.path(subdir ++ "/win"),
b.path(subdir ++ "/win/rc"),
b.path(subdir ++ "/generic"),

61
src/buildsuites/suite_tcl90/suite.tcl

@ -233,7 +233,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 build_tk patches src tools} {
foreach item {build905.zig build_common.zig build_libtommath build_zlib build_tclvfs build_tclthread build_tk patches src tools} {
file delete -force [file join $builddir $item]
file copy [file join $suiteroot $item] $builddir
}
@ -277,68 +277,25 @@ foreach name {tcl tclthread tclvfs tk tklib tcllib} {
fossil_source $name [dict get $s url] $ref [file join $stage [dict get $s dir]]
}
#zlib: use the tcl tree's own vendored copy (version-matched; includes contrib/minizip)
set zlibdir [file join $stage zlib]
if {![file isdirectory $zlibdir]} {
log "staging zlib from tcl compat/zlib"
file copy [file join $stage tcl905 compat zlib] $zlibdir
}
#zlib: the recipe compiles the tcl tree's own vendored copy in place
#(tcl905/compat/zlib, version-matched, includes contrib/minizip) - nothing to stage.
#vqtcl/vlerq: removed from the suite (user 2026-07-20) - old-tclkit experiment with no
#confirmed public upstream. Future tclkit work: TEMP_REFERENCE/metakit + KitCreator.
#-- generated files a fresh tcl checkout lacks (normally configure/prepare products) --
#-- version facts for smokes/logging ---------------------------------------
#G-102: the configure-products a fresh checkout lacks (tclUuid.h, tclsh.exe.manifest,
#the thread.rc tcl.h include, tkUuid.h, wish.exe.manifest) are now generated INSIDE
#the zig recipe as build-cache overlays - source checkouts are never written. What
#remains here is read-only: version facts for the smoke expectations and the log.
set tcldir [file join $stage tcl905]
#tclUuid.h: '#define TCL_VERSION_UUID \' + manifest.uuid content
set f [open [file join $tcldir manifest.uuid] r]; set uuid [read $f]; close $f
set f [open [file join $tcldir win tclUuid.h] w]
fconfigure $f -translation lf
puts -nonewline $f "#define TCL_VERSION_UUID \\\n$uuid"
close $f
#tclsh.exe.manifest from .in: TCL_WIN_VERSION = TCL_VERSION . releaselevel . patchlevel-stripped
set f [open [file join $tcldir generic tcl.h] r]; set tclh [read $f]; close $f
if {![regexp {#\s*define\s+TCL_VERSION\s+"([^"]+)"} $tclh -> tclversion]} {fail "TCL_VERSION not found in tcl.h"}
if {![regexp {#\s*define\s+TCL_PATCH_LEVEL\s+"([^"]+)"} $tclh -> patchlevel]} {fail "TCL_PATCH_LEVEL not found in tcl.h"}
set rlevel [expr {[string match *a* $patchlevel] ? 0 : ([string match *b* $patchlevel] ? 1 : 2)}]
set winver "$tclversion.$rlevel.[string map {a {} b {} . {}} $patchlevel]"
set f [open [file join $tcldir win tclsh.exe.manifest.in] r]; set manifest [read $f]; close $f
set manifest [string map [list @TCL_WIN_VERSION@ $winver @MACHINE@ AMD64] $manifest]
set f [open [file join $tcldir win tclsh.exe.manifest] w]
fconfigure $f -translation lf
puts -nonewline $f $manifest
close $f
log "generated tclUuid.h + tclsh.exe.manifest (TCL_WIN_VERSION $winver, patchlevel $patchlevel)"
#thread.rc: the zig rc compile does not receive the makefile's include paths; give the
#version resource script the tcl header it needs (idempotent scripted edit of the
#staged checkout - the historical recipe carried this as an uncommitted tweak).
set rcfile [file join $stage tclthread win thread.rc]
set f [open $rcfile r]; set rc [read $f]; close $f
if {![string match "*tcl905/generic/tcl.h*" $rc]} {
set rc [string map [list "#include <winver.h>" "#include <winver.h>\n#include \"../../tcl905/generic/tcl.h\""] $rc]
set f [open $rcfile w]; fconfigure $f -translation lf; puts -nonewline $f $rc; close $f
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)"
log "sources: tcl $patchlevel (checkout [string range $uuid 0 11]...), tk $tkpatchlevel"
#-- build -----------------------------------------------------------------
set zigv [exec $opt(-zig) version]

Loading…
Cancel
Save