You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2851 lines
146 KiB

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;
// ********************
// tclvfs experiments
//const tclvfs_static = @import("build_tclvfs/build_tclvfs_static.zig");
const tclvfs_shared = @import("build_tclvfs/build_tclvfs_shared.zig");
// ********************
//vqtcl/vlerq removed from the suite (old-tclkit experiment; see note near the former
//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_tk = @import("build_tk/build_tk.zig").build_tk; //G-098
const TkBuild = @import("build_tk/build_tk.zig").TkBuild; //G-103
const tcl_major_version = "9";
const tcl_nodot_version = "90"; //VER in makefile
const tcl_dot_version = "9.0"; //VERSION in makefile
const tcl_patch_suffix = "0";
const tcl_source_folder = "../tcl905";
const tcl_patch_level = tcl_dot_version ++ tcl_patch_suffix; //e.g 9.0b2 - generic/tcl.h TCL_PATCH_LEVEL (note TCL_PATCH_LEVEL in configure may be different e.g just the suffix "b2")
// e.g result: tclsh90.exe & tclsh90s.exe
const tclsh_shared = "tclsh" ++ tcl_nodot_version; //leave off exe suffix - zig adds .exe automatically for windows
const tclsh_static = "tclsh" ++ tcl_nodot_version ++ "s";
//jmn3
var static_build: bool = true;
//minimum zig version
//const req_zig_version = "0.13.0";
//comptime {
// const req_zig = std.SemanticVersion.parse(req_zig_version) catch unreachable;
// if (builtin.zig_version.order(req_zig) == .lt) {
// @compileError(std.fmt.comptimePrint(
// "Your Zig version v{} does not meet the minimum build requirement of v{}",
// .{ builtin.zig_version, req_zig },
// ));
// }
//}
comptime {
//The suite pin (G-102 acceptance: the recipe records the zig version it is
//written against). The recipe is 0.16-API and does not build under 0.14/0.15;
//punk-getzig materializes the pinned toolchain (see README Pinned zig).
const required_zig = "0.16.0";
const current_zig = builtin.zig_version;
const min_zig = std.SemanticVersion.parse(required_zig) catch unreachable;
if (current_zig.order(min_zig) == .lt) {
const error_message =
\\This recipe is written against zig {} (see README Pinned zig) and
\\does not build with older zigs. bin/punk-getzig fetches the pinned
\\toolchain; https://ziglang.org/download/ hosts releases.
\\
;
@compileError(std.fmt.comptimePrint(error_message, .{min_zig}));
}
}
const targets: []const std.Target.Query = &.{
//.{ .cpu_arch = .aarch64, .os_tag = .macos },
//.{ .cpu_arch = .aarch64, .os_tag = .linux },
//.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
// tried this^ - built but:
// apparently when linking against musl libc for fully static binaries, the dlopen function will only be a stub
// which stops us loading binary modules.
//From andrewrk on https://github.com/luvit/luvi/issues/231
//The other option zig cc provides you is "static except glibc" builds,
//and if you pick an old enough glibc version (e.g. matching holy-build-box)
//then your binary tarballs will work on any linux distro that uses glibc and
//standard dynamic linker path (which is most). This is the $arch-linux-gnu target.
.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .gnu },
.{ .cpu_arch = .x86_64, .os_tag = .windows },
};
pub fn build(b: *std.Build) !void {
//G-102 invocation modes. This recipe serves two build roots:
// STAGED mode - build root is the staged recipe copy (_build/<suite>/build) with
// the source trees materialized beside it (../tcl905 etc) either by suite.tcl's
// fossil flow or by this recipe's own bootstrap staging. Full build graph.
// BOOTSTRAP mode - build root is the TRACKED suite dir (sources.config present,
// no ../tcl905): only the staging steps are registered - 'zig build stage'
// materializes the pinned build.zig.zon sources + a recipe copy into the
// _build stage, and 'zig build bootstrap' chains the staged build after it.
// This is the no-tclsh entry point.
if (!common.pathExists(b, tcl_source_folder)) {
if (common.pathExists(b, "sources.config")) {
try bootstrapMode(b);
return;
}
@panic("suite recipe: no source trees found beside the recipe (" ++ tcl_source_folder ++
" missing). Use 'zig build stage' (or bootstrap) from the tracked suite dir for the " ++
"pinned zon flow, or 'tclsh suite.tcl build' for the fossil dev flow.");
}
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
//set up and display some of the same prefix-dependent vars as Makefile for maintainability
const prefix = try std.fmt.allocPrint(b.allocator, "{s}", .{b.install_path});
const exec_prefix = prefix;
std.debug.print("prefix {s}\n", .{prefix});
std.debug.print("exec_prefix {s}\n", .{exec_prefix});
const bindir = b.pathJoin(&.{ prefix, "bin" });
std.debug.print("bindir {s}\n", .{bindir});
const libdir = b.pathJoin(&.{ prefix, "lib" }); //TCL_PACKAGE_PATH ?
std.debug.print("libdir {s}\n", .{libdir});
const includedir = b.pathJoin(&.{ prefix, "include" });
std.debug.print("includedir {s}\n", .{includedir});
const datarootdir = b.pathJoin(&.{ prefix, "share" });
std.debug.print("datarootdir {s}\n", .{datarootdir});
const runstatedir = b.pathJoin(&.{ prefix, "run" });
std.debug.print("runstatedir {s}\n", .{runstatedir});
const mandir = b.pathJoin(&.{ prefix, "man" });
std.debug.print("mandir {s}\n", .{mandir});
const tcl_library = b.pathJoin(&.{ prefix, "lib", "tcl" ++ tcl_dot_version });
std.debug.print("tcl_library {s}\n", .{tcl_library});
const bin_install_dir = bindir;
std.debug.print("bin_install_dir {s}\n", .{bin_install_dir});
const lib_install_dir = libdir; //TCL_PACKAGE_PATH ?
std.debug.print("lib_install_dir {s}\n", .{lib_install_dir});
const script_install_dir = tcl_library;
std.debug.print("script_install_dir {s}\n", .{script_install_dir});
const script_install_dir_rel = b.pathJoin(&.{ "lib", "tcl" ++ tcl_dot_version });
const module_install_dir = b.pathJoin(&.{ script_install_dir, "..", "tcl" ++ tcl_major_version });
std.debug.print("module_install_dir {s}\n", .{module_install_dir});
const module_install_dir_rel = b.pathJoin(&.{ script_install_dir_rel, "..", "tcl" ++ tcl_major_version });
const include_install_dir = tcl_library;
std.debug.print("include_install_dir {s}\n", .{include_install_dir});
//target.result.os.tag = .
//mac dylib?
var tcl_shlib_ext: []const u8 = undefined;
switch (target.result.os.tag) {
.windows => {
tcl_shlib_ext = try std.fmt.allocPrint(b.allocator, "{s}", .{".dll"});
},
.macos => {
tcl_shlib_ext = try std.fmt.allocPrint(b.allocator, "{s}", .{".dylib"});
},
else => {
tcl_shlib_ext = try std.fmt.allocPrint(b.allocator, "{s}", .{".so"});
},
}
const tcl_dll_file = try std.fmt.allocPrint(b.allocator, "tcl" ++ tcl_nodot_version ++ "{s}", .{tcl_shlib_ext});
std.debug.print("tcl_dll_file {s}\n", .{tcl_dll_file});
//const tcl_lib_file = "libtcl" ++ tcl_nodot_version ++ ".dll.a";
const tcl_lib_file = try std.fmt.allocPrint(b.allocator, "libtcl" ++ tcl_nodot_version ++ "{s}" ++ ".a", .{tcl_shlib_ext});
std.debug.print("tcl_lib_file {s}\n", .{tcl_lib_file});
const tcl_zip_file = "libtcl" ++ tcl_dot_version ++ tcl_patch_suffix ++ ".zip";
std.debug.print("tcl_zip_file {s}\n", .{tcl_zip_file});
const tcl_vfs_path = b.pathJoin(&.{ "libtcl.vfs", "tcl_library" });
std.debug.print("tcl_vfs_path {s}\n", .{tcl_vfs_path});
const tcl_vfs_root = "libtcl.vfs";
std.debug.print("tcl_vfs_root {s}\n", .{tcl_vfs_root});
const tcl_stub_lib_file = "libtclstub.a";
std.debug.print("tcl_stub_lib_file {s}\n", .{tcl_stub_lib_file});
//const tcl_stub_lib_file = "libtclstub87.a";
const dde_dll_file = "tcl9dde14.dll";
std.debug.print("dde_dll_file {s}\n", .{dde_dll_file});
const dde_dll_file8 = "tcldde14.dll";
std.debug.print("dde_dll_file8 {s}\n", .{dde_dll_file8});
const reg_dll_file = "tcl9registry13.dll";
std.debug.print("reg_dll_file {s}\n", .{reg_dll_file});
const reg_dll_file8 = "tclregistry13.dll";
std.debug.print("reg_dll_file8 {s}\n", .{reg_dll_file8});
//pass the subdirectory as the build_tcl90/build_libtommath sees it ?
//const tommath_sourcedir = b.pathJoin(&.{ "..", "tcl" ++ tcl_nodot_version, "libtommath" });
const tommath_sourcedir = tcl_source_folder ++ "/libtommath";
//var tommath_sourcedir: []const u8 = undefined;
//if (target.result.os.tag == .windows) {
// //tommath_sourcedir = tcl_source_folder ++ "/libtommath";
// tommath_sourcedir = b.pathJoin(&.{ "..", "tcl" ++ tcl_nodot_version, "libtommath" });
//} else {
// //tommath_sourcedir = b.pathJoin(&.{ "..", "libtommath" });
// tommath_sourcedir = b.pathJoin(&.{ "..", "tcl" ++ tcl_nodot_version, "libtommath" });
//}
//const tommath_lib_compile = try build_tommath(tommath_sourcedir, b, target, optimize);
const zlib_sourcedir = tcl_source_folder ++ "/compat/zlib";
//const zlib_sourcedir = "../zlib" ++ "/compat/zlib";
var zlib_lib_compile: *std.Build.Step.Compile = undefined;
zlib_lib_compile = try build_zlib(zlib_sourcedir, b, target, optimize);
//if (target.result.os.tag != .windows) {
//}
// ================================================
//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(.{
.root_source_file = b.path("tools/wrapfiletofile.zig"),
.target = b.graph.host,
}),
});
const wrap_run = b.addRunArtifact(wrap_exe);
wrap_run.addArgs(&.{
"-prefix",
"#define TCL_VERSION_UUID \\",
"-prefixnl",
"1",
"-input",
});
wrap_run.addFileArg(b.path(tcl_source_folder ++ "/manifest.uuid"));
wrap_run.addArg("-output");
const tcluuid_file1 = wrap_run.addOutputFileArg("tclUuid.h");
//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");
//Runs of the built (non-zip) shell execute the CACHED artifact, which has no
//../lib/tcl9.0 beside it - point such runs at the source tree's own script
//library so init.tcl resolves (the zip shell's runs must NOT get this: its
//attached library is the thing under test).
const tcl_library_src = common.replaceAll(b, b.pathFromRoot(tcl_source_folder ++ "/library"), "\\", "/");
// ================================================
var ac_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer ac_flags.deinit();
try ac_flags.appendSlice(&.{
"-pipe",
"-finput-charset=UTF-8",
"-DPACKAGE_NAME=\"tcl\"",
"-DPACKAGE_TARNAME=\"tcl\"",
"-DPACKAGE_VERSION=\"" ++ tcl_dot_version ++ "\"",
"-DPACKAGE_STRING=\"tcl " ++ tcl_dot_version ++ "\"",
"-DPACKAGE_BUGREPORT=\"\"",
"-DPACKAGE_URL=\"\"",
"-DTCL_CFGVAL_ENCODING=\"utf-8\"",
//WARNING - it is not enough to set TCL_COMPILE_DEBUG & TCL_COMPILE_STATS to 0 - if they exist they will affect performance
//"-DTCL_COMPILE_DEBUG=0",
//"-DTCL_COMPILE_STATS=0",
"-DTCL_CFG_DO64BIT=1",
"-DHAVE_NO_SEH=1", //required for tclWinFCmd.c or we get undeclared identifier '__try'
//JMN2
//"-DHAVE_CPUID=1",
//"-DHAVE_STDLIB_H=1",
//"-DHAVE_STRING_H=1",
//"-DHAVE_INTTYPES_H=1",
//"-DHAVE_STDINT_H=1",
//"-DHAVE_STRINGS_H=1",
//"-DHAVE_SYS_STAT_H=1",
//"-DHAVE_SYS_TYPES_H=1",
//"-DHAVE_UNISTD_H=1", //probably needs to be 0 if using msvc ?
//"-DSTDC_HEADERS=1",
//"-DNDEBUG=1",
//"-DHAVE_STDBOOL_H=1",
//"-DHAVE_CAST_TO_UNION=1",
//"-DHAVE_INTPTR_T=1",
//"-DHAVE_UINTPTR_T=1",
//"-DHAVE_INTRIN_H=1",
"-DMP_64BIT=1",
"-DHAVE_ZLIB=1",
"-DZIPFS_BUILD=1", //JMN todo
"-DTCL_CFG_OPTIMIZED=1",
"-DTCL_THREADS=1", //probably not required
try std.fmt.allocPrint(b.allocator, "-DTCL_PACKAGE_PATH=\"{s}\"", .{lib_install_dir}),
});
//"-DTCL_NO_DEPRECATED",
//"-fno-lto",
//"-DMODULE_SCOPE=extern",
if (target.result.os.tag == .windows) {
try ac_flags.appendSlice(&.{
//"-DTCL_WITH_EXTERNAL_TOMMATH", //required for windows when using tcltommath that *doesn't* come with tcl (?)
"-DHAVE_STDIO_H=1",
"-DMODULE_SCOPE=extern",
"-DHAVE_WSPIAPI_H=1", //windows specific?
});
//jjj
//try ac_flags.appendSlice(&.{
// "-DHAVE_STDIO_H=1",
// "-DHAVE_STDLIB_H=1",
// "-DHAVE_STRING_H=1",
// "-DHAVE_INTTYPES_H=1",
// "-DHAVE_STDINT_H=1",
// "-DHAVE_STRINGS_H=1",
// "-DHAVE_SYS_STAT_H=1",
// "-DHAVE_SYS_TYPES_H=1",
// "-DHAVE_UNISTD_H=1",
// "-DHAVE_SYS_TIME_H=1",
// //
// "-DNO_SYS_WAIT_H=1",
// "-DNO_DLFCN_H=1",
// "-DHAVE_SYS_PARAM_H=1",
// //
// "-DHAVE_STDBOOL_H=1",
// //
//});
//added to try to fix 'linsert {} 0 a b' crash
//try ac_flags.appendSlice(&.{
// "-DSTDC_HEADERS=1",
// "-D_REENTRANT=1",
// "-D_THREAD_SAFE=1",
// "-DHAVE_PTHREAD_ATTR_SETSTACKSIZE=1",
// "-DHAVE_DECL_PTHREAD_MUTEX_RECURSIVE=1",
// //
// "-DMODULE_SCOPE=extern",
// "-DHAVE_CAST_TO_UNION=1",
// "-DNDEBUG=1",
// "-DMP_PREC=4",
// "-D_FILE_OFFSET_BITS=64",
// "-DHAVE_LSEEK64=1",
// "-DHAVEGETCWD=1",
// "-DHAVE_MKSTEMP=1",
// "-DNO_GETWD=1",
// "-DNO_WAIT3=1",
// "-DNO_FORK=1",
// "-DNO_MKNOD=1",
// "-DNO_TCDRAIN=1",
// "-DNO_UNAME=1",
// "-DNOREALPATH=1",
// "-DNEED_FAKE_RFC2553=1",
// "-DHAVE_DECL_GETHOSTBYNAME_R=0",
// "-DHAVE_DECL_GETHOSTBYADDR_R=0",
// "-DNO_FD_SET=1",
// //
// "-DHAVE_MKTIME=1",
// "-DHAVE_TIMEZONE_VAR=1",
// "-DHAVE_STRUCT_STAT_ST_RDEV=1",
// "-DNO_FSTATFS=1",
// "-Duid_t=int",
// "-Dgid_t=int",
// "-DHAVE_INTPTR_T=1",
// "-DHAVE_UINTPTR_T=1",
// "-DNO_UNION_WAIT=1",
// "-DHAVE_SIGNED_CHAR=1",
// "-DHAVE_PUTENV_THAT_COPIES=1",
// "-DTCL_UNLOAD_DLLS=1",
// "-DHAVE_CPUID=1",
//});
//try ac_flags.appendSlice(&.{
// "-Dsocklen_t=int",
//});
//-DHAVE_ZLIB being deprecated 2024-10
try ac_flags.appendSlice(&.{
"-DHAVE_ZLIB=1",
"-DZIPFS_BUILD=1",
//"-DSUPPORT_BUILTIN_ZIP_INSTALL=1", // won't compile win 2024-10-11 - bug reported
});
try ac_flags.appendSlice(&.{
// should be for static build only(?)
"-DTCL_WITH_INTERNAL_ZLIB",
"-DTCL_TOMMATH",
});
} else {
try ac_flags.appendSlice(&.{
//"-DTCL_WITH_EXTERNAL_TOMMATH",
"-DHAVE_MEMORY_H=1",
"-DMODULE_SCOPE=extern __attribute__((__visibility__(\"hidden\")))",
"-DHAVE_HIDDEN=1",
"-DHAVE_SYS_PARAM_H=1",
"-DUSE_THREAD_ALLOC=1",
"-D_REENTRANT=1",
"-D_THREAD_SAFE=1",
"-DHAVE_PTHREAD_ATTR_SETSTACKSIZE=1",
"-DHAVE_PTHREAD_ATFORK=1",
"-D_LARGEFILE64_SOURCE=1",
"-DTCL_WIDE_INT_IS_LONG=1",
"-DHAVE_GETCWD=1",
"-DHAVE_MKSTEMP=1",
"-DHAVE_OPENDIR=1",
"-DHAVE_STRTOL=1",
"-DHAVE_WAITPID=1",
"-DHAVE_GETNAMEINFO=1",
"-DHAVE_GETADDRINFO=1",
"-DHAVE_FREEADDRINFO=1",
"-DHAVE_GAI_STRERROR=1",
"-DHAVE_STRUCT_ADDRINFO=1",
"-DHAVE_STRUCT_IN6_ADDR=1",
"-DHAVE_STRUCT_SOCKADDR_IN6=1",
"-DHAVE_STRUCT_SOCKADDR_STORAGE=1",
"-DHAVE_GETPWUID_R_5=1",
"-DHAVE_GETPWUID_R=1",
"-DHAVE_GETPWNAM_R_5=1",
"-DHAVE_GETPWNAM_R=1",
"-DHAVE_GETGRGID_R_5=1",
"-DHAVE_GETGRGID_R=1",
"-DHAVE_GETGRNAME_R_5=1",
"-DHAVE_GETGRNAME_R=1",
"-DHAVE_DECL_GETHOSTBYNAME_R=1",
"-DHAVE_GETHOSTBYNAME_R_6=1",
"-DHAVE_GETHOSTBYNAME_R=1",
"-DHAVE_DECL_GETHOSTBYADDR_R=1",
"-DHAVE_GETHOSTBYADDR_R_8=1",
"-DHAVE_GETHOSTBYADDR_R=1",
"-DHAVE_TERMIOS_H=1",
"-DHAVE_SYS_IOCTL_H=1",
"-DHAVE_SYS_TIME_H=1",
"-DTIME_WITH_SYS_TIME=1",
"-DHAVE_GMTIME_R=1",
"-DHAVE_LOCALTIME_R=1",
"-DHAVE_TM_GMTOFF=1",
"-DHAVE_TIMEXONE_VAR=1",
"-DHAVE_STRUCT_STAT_ST_BLOCKS=1",
"-DHAVE_STRUCT_STAT_ST_BLKSIZE=1",
"-DHAVE_BLKCNT_T=1",
"-DNO_UNION_WAIT=1",
"-DHAVE_SIGNED_CHAR=1",
"-DHAVE_LANGINFO=1",
"-DHAVE_MKSTEMPS=1",
//"-DHAVE_FTS=1",
"-DTCL_UNLOAD_DLLS=1",
//"-fPIC",
try std.fmt.allocPrint(b.allocator, "-DTCL_SHLIB_EXT=\"{s}\"", .{tcl_shlib_ext}),
try std.fmt.allocPrint(b.allocator, "-DTCL_LIBRARY=\"{s}\"", .{tcl_library}),
});
}
//msvc - test
//try ac_flags.appendSlice(&.{
// "-Dinline=__inline",
// "-D_CRT_SECURE_NO_DEPRECATE",
// "-D_CRT_NONSTDC_NO_DEPRECATE"
//});
//"-Wl,-input-charset=UTF-8",
//"-finput-charset=UTF-8"
var warning_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer warning_flags.deinit();
try warning_flags.appendSlice(&.{
"-Wall",
"-Wextra",
"-Wshadow",
"-Wundef",
"-Wwrite-strings",
"-Wpointer-arith",
"-Wc++-compat",
"-fextended-identifiers",
});
//"-Werror=return-type",
var optimize_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer optimize_flags.deinit();
//windows?
//try optimize_flags.appendSlice(&.{
// "-O0",
// "-fomit-frame-pointer",
// //"-g0",
//});
//try optimize_flags.appendSlice(&.{
// "-O3",
// "-fomit-frame-pointer",
// //"-g0",
//});
try optimize_flags.appendSlice(&.{
"-O3", //??
//"-O2",
"-fomit-frame-pointer",
//"-g",
"-finput-charset=UTF-8",
});
//"-fomit-frame-pointer",
var c_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer c_flags.deinit();
try c_flags.appendSlice(optimize_flags.items);
try c_flags.appendSlice(&.{
"-DMP_FIXED_CUTOFFS",
"-fno-sanitize=undefined", //required on windows to fix: linsert {} 0 a a crash
"-fno-sanitize-trap=undefined",
//"-DMP_NO_STDINT",
//"-D__USE_MINGW_ANSI_STDIO=0",
//"-DTCL_MEM_DEBUG", //must be defined for all modules, or undefined for all modules that are going to be linked together
});
if (target.result.os.tag == .linux) {
//required to avoid error on linux-musl (at least) - review - check other targets
try c_flags.appendSlice(&.{
"-fno-sanitize=undefined",
});
//without it, gdb gives:
//Program received signal SIGILL, Illegal instruction.
//0x0000000001b6c0e6 in Tcl_UniCharToUtfDString (uniStr=0x7fffffffd605, uniLength=0, dsPtr=0x7fffffffcc00) at /mnt/c/buildtcl/2024zig/tcl90/generic/tclUtf.c:332
//332 while (*w != '\0') {
}
var config_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer config_flags.deinit();
//try config_flags.appendSlice(base_flags.items);
//can't use ++ for non comptime values such as libdir,bindir...
try config_flags.appendSlice(&.{
try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_LIBDIR=\"{s}\"", .{libdir}),
try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_BINDIR=\"{s}\"", .{bindir}),
try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_INCDIR=\"{s}\"", .{includedir}),
try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_SCRDIR=\"{s}\"", .{tcl_library}),
try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_DOCDIR=\"{s}\"", .{mandir}),
});
//todo - set default install dirs: windows c:/tcl90, bsd /usr/local/tcl90?, linux /opt/tcl90?
try config_flags.appendSlice(&.{
try std.fmt.allocPrint(b.allocator, "-DCFG_INSTALL_LIBDIR=\"{s}\"", .{libdir}),
try std.fmt.allocPrint(b.allocator, "-DCFG_INSTALL_BINDIR=\"{s}\"", .{bindir}),
try std.fmt.allocPrint(b.allocator, "-DCFG_INSTALL_INCDIR=\"{s}\"", .{includedir}),
try std.fmt.allocPrint(b.allocator, "-DCFG_INSTALL_SCRDIR=\"{s}\"", .{tcl_library}),
try std.fmt.allocPrint(b.allocator, "-DCFG_INSTALL_DOCDIR=\"{s}\"", .{mandir}),
});
//CFG_RUNTIME_DLLFILE used also on unix? seems to be in generic/tclPkgConfig.c
try config_flags.appendSlice(&.{
//"-DCFG_RUNTIME_DLLFILE=\"tcl" ++ tcl_nodot_version ++ ".dll\"",
try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_DLLFILE=\"{s}\"", .{tcl_dll_file}),
});
//BUILD_<somelibrary> needs to be defined for windows
if (target.result.os.tag == .windows) {
try config_flags.appendSlice(&.{
"-DBUILD_tcl",
});
}
//CC_SWITCHES = -I"${BUILD_DIR}" -I"${GENERIC_DIR_NATIVE}" -I"${TOMMATH_DIR_NATIVE}" \
//-I"${ZLIB_DIR_NATIVE}" -I"${WIN_DIR_NATIVE}" \
//${CFLAGS} ${CFLAGS_WARNING} ${SHLIB_CFLAGS} -DMP_PREC=4 \
//${AC_FLAGS} ${COMPILE_DEBUG_FLAGS} ${NO_DEPRECATED_FLAGS}
var cc_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer cc_flags.deinit();
try cc_flags.appendSlice(c_flags.items);
try cc_flags.appendSlice(warning_flags.items);
try cc_flags.appendSlice(config_flags.items);
try cc_flags.appendSlice(&.{
"-DMP_PREC=4",
});
try cc_flags.appendSlice(ac_flags.items);
var base_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer base_flags.deinit();
try base_flags.appendSlice(&.{
"-municode",
"-Wl,--subsystem,windows",
//"-DTCL_MEM_DEBUG", //must be defined for all modules, or undefined for all modules that are going to be linked together
});
if (target.result.os.tag == .windows) {
//TODO win64-arm, win32(?)
//b.installFile(tcl_source_folder ++ "/compat/zlib/win64/zlib1.dll", "bin/zlib1.dll");
b.installFile(tcl_source_folder ++ "/libtommath/win64/libtommath.dll", "bin/libtommath.dll");
//TODO - flag to allow independent building of static vs shared build to give failure independence
// if STATIC_BUILD
//b.installFile(tcl_source_folder ++ "/compat/zlib/win64/zlib1.dll", "lib/zdll.lib");
b.installFile(tcl_source_folder ++ "/libtommath/win64/tommath.lib", "lib/tommath.lib");
}
// ================================================================
//tcl8.7?
//STUB_OBJS = \
// tclStubLib.$(OBJEXT) \
// tclTomMathStubLib.$(OBJEXT) \
// tclOOStubLib.$(OBJEXT) \
// tclWinPanic.$(OBJEXT)
//tcl9
//STUB_OBJS =
//tclStubLib.$(OBJEXT)
//tclStubCall.$(OBJEXT)
//tclStubLibTbl.$(OBJEXT)
//tclTomMathStubLib.$(OBJEXT)
//tclOOStubLib.$(OBJEXT)
//tclWinPanic.$(OBJEXT)
const finalstublib = b.addLibrary(.{
.linkage = .static,
.name = if (target.result.os.tag == .windows) "libtclstub" else "tclstub",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
var stublib_flags = std.array_list.Managed([]const u8).init(b.allocator);
try stublib_flags.appendSlice(cc_flags.items);
try stublib_flags.appendSlice(&.{
"-DSTATIC_BUILD",
"-fno-lto",
});
//finalstublib.root_module.addIncludePath(.{ .path = tcl_source_folder ++ "/generic" });
//finalstublib.root_module.addIncludePath(.{ .src_path = tcl_source_folder ++ "/generic" });
finalstublib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
if (target.result.os.tag == .windows) {
//finalstublib.root_module.addIncludePath(.{ .path = tcl_source_folder ++ "/win" });
finalstublib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
} else {
//finalstublib.root_module.addIncludePath(.{ .path = tcl_source_folder ++ "/unix" });
finalstublib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
}
if (target.result.os.tag == .windows) {
finalstublib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/libtommath"));
finalstublib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib"));
} else {
finalstublib.root_module.addIncludePath(b.path(tommath_sourcedir));
finalstublib.root_module.addIncludePath(b.path(zlib_sourcedir));
}
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLib.c"),
.flags = stublib_flags.items,
});
//tcl9
var stublib_call_flags = std.array_list.Managed([]const u8).init(b.allocator);
try stublib_call_flags.appendSlice(cc_flags.items);
try stublib_call_flags.appendSlice(config_flags.items);
try stublib_call_flags.appendSlice(&.{
"-DSTATIC_BUILD",
//try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_BINDIR=\"{s}\"", .{ bindir }),
});
//if (target.result.os.tag == .windows) {
// try stublib_call_flags.appendSlice(&.{
// try std.fmt.allocPrint(b.allocator, "-DCFG_RUNTIME_DLLFILE=\"{s}\"", .{ tcl_dll_file }),
// });
//}
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubCall.c"),
.flags = stublib_call_flags.items,
});
//tcl9
var stublib_tbl_flags = std.array_list.Managed([]const u8).init(b.allocator);
try stublib_tbl_flags.appendSlice(cc_flags.items);
try stublib_tbl_flags.appendSlice(&.{
"-DSTATIC_BUILD",
});
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLibTbl.c"),
.flags = stublib_tbl_flags.items,
});
var stublib_tom_flags = std.array_list.Managed([]const u8).init(b.allocator);
try stublib_tom_flags.appendSlice(cc_flags.items);
try stublib_tom_flags.appendSlice(&.{
"-fno-lto",
});
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclTomMathStubLib.c"),
.flags = stublib_tom_flags.items,
});
var stublib_oo_flags = std.array_list.Managed([]const u8).init(b.allocator);
try stublib_oo_flags.appendSlice(cc_flags.items);
try stublib_oo_flags.appendSlice(&.{
"-fno-lto",
});
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclOOStubLib.c"),
.flags = stublib_oo_flags.items,
});
var winpaniclib_flags = std.array_list.Managed([]const u8).init(b.allocator);
try winpaniclib_flags.appendSlice(cc_flags.items);
try winpaniclib_flags.appendSlice(&.{
"-DSTATIC_BUILD",
});
if (target.result.os.tag == .windows) {
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinPanic.c"),
.flags = winpaniclib_flags.items,
});
}
var windde_obj_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer windde_obj_flags.deinit();
try windde_obj_flags.appendSlice(cc_flags.items);
try windde_obj_flags.appendSlice(&.{});
var winreg_obj_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer winreg_obj_flags.deinit();
try winreg_obj_flags.appendSlice(cc_flags.items);
try winreg_obj_flags.appendSlice(&.{});
if (target.result.os.tag == .windows) {
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinDde.c"),
.flags = windde_obj_flags.items,
});
finalstublib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinReg.c"),
.flags = winreg_obj_flags.items,
});
}
finalstublib.root_module.link_libc = true;
b.installArtifact(finalstublib);
// ================================================================
// Special case object targets
const winpaniclib = b.addObject(.{
.name = "tclWinPanic",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
if (target.result.os.tag == .windows) {
winpaniclib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic/"));
winpaniclib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win/"));
winpaniclib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/libtommath/"));
winpaniclib.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/"));
winpaniclib.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinPanic.c"),
.flags = winpaniclib_flags.items,
});
winpaniclib.root_module.link_libc = true;
}
if (target.result.os.tag == .windows) {
const winreg_obj = b.addObject(.{
.name = "tclWinReg",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
winreg_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic/"));
winreg_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win/"));
winreg_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/libtommath/"));
winreg_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/"));
winreg_obj.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinReg.c"),
.flags = winreg_obj_flags.items,
});
winreg_obj.root_module.link_libc = true;
//
const windde_obj = b.addObject(.{
.name = "tclWinDde",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
windde_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic/"));
windde_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win/"));
windde_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/libtommath/"));
windde_obj.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/"));
windde_obj.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinDde.c"),
.flags = windde_obj_flags.items,
});
windde_obj.root_module.link_libc = true;
//DDE_OBJS
}
// for linking to Tclsh
var appinit_path: []const u8 = undefined;
if (target.result.os.tag == .windows) {
appinit_path = tcl_source_folder ++ "/win/tclAppInit.c";
} else {
appinit_path = tcl_source_folder ++ "/unix/tclAppInit.c";
}
//end special case object targets
// ------------------------------------------------------------------
//const stubarchive_install_step = &b.addInstallFileWithDir(stubarchive.getEmittedBin(), .prefix, "libtclstubXX.lib").step;
//b.getInstallStep().dependOn(&b.addInstallFileWithDir(stubarchive.getEmittedBin(),.prefix,"libtclstubXX.a").step);
//b.getInstallStep().dependOn(stubarchive_install_step);
const wf = b.addWriteFiles();
const stublib_file = b.fmt("buildarchive\\{s}", .{finalstublib.out_filename});
const lz_stublib_file = wf.addCopyFile(finalstublib.getEmittedBin(), stublib_file);
//s flag to ar should be equivalent to running ranlib
const create_stublib_archive = b.addSystemCommand(&.{
"zig",
"ar",
"crs",
"--",
});
//no tcl_nodot_version for tcl9 libtclstub.a
const stublib_archive_file = create_stublib_archive.addOutputFileArg("buildarchive\\" ++ tcl_stub_lib_file);
//const out_ranlib = run_ranlib.addOutputFileArg("buildarchive\\tclstub.lib");
create_stublib_archive.addFileArg(lz_stublib_file);
const installed_stublib_archive = b.addInstallFileWithDir(stublib_archive_file, .prefix, "lib\\" ++ tcl_stub_lib_file);
//const stublib_archive = b.addInstallFileWithDir(out_ranlib , .prefix, "lib\\tclstub.lib");
installed_stublib_archive.step.dependOn(&create_stublib_archive.step);
//b.getInstallStep().dependOn(&stublib_archive.step);
//dde_dll_file and reg_dll_file need to be added to the zip file
var dll_flags = std.array_list.Managed([]const u8).init(b.allocator);
//try dll_flags.appendSlice(base_flags.items);
try dll_flags.appendSlice(&.{
"-pipe",
"-municode",
});
if (target.result.os.tag == .windows) {
try dll_flags.appendSlice(&.{
"-Wl,--enable-auto-image-base",
"-static-libgcc",
});
} else {}
//"-Wl,-static-libgcc",
//std.fs.path.stem(dde_dll_file) ;// tcl9dde14.dll - tcl9
//std.fs.path.stem(dde_dll_file8) ;// tcldde14.dll - tcl8
const dde_dll = b.addLibrary(.{
.linkage = .dynamic,
.name = std.fs.path.stem(dde_dll_file),
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
dde_dll.step.dependOn(&installed_stublib_archive.step);
dde_dll.step.dependOn(&finalstublib.step);
var install_dde_dll: *std.Build.Step.InstallArtifact = undefined;
if (target.result.os.tag == .windows) {
dde_dll.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic/"));
dde_dll.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win/"));
dde_dll.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/TclWinDde.c"),
.flags = dll_flags.items,
});
//LIBS
//dde_dll.root_module.addLibraryPath(stublib_archive.source.dirname());
dde_dll.root_module.addObjectFile(finalstublib.getEmittedBin());
//dde_dll.root_module.addObjectFile(installed_stublib_archive.source);
//dde_lib.root_module.linkSystemLibrary("tclstub" ++ tcl_nodot_version); //libtclstubXX.a
//dde_lib.root_module.linkSystemLibrary("tclstub", .{}); //tcl9
dde_dll.root_module.linkSystemLibrary("netapi32", .{});
dde_dll.root_module.linkSystemLibrary("kernel32", .{});
dde_dll.root_module.linkSystemLibrary("user32", .{});
dde_dll.root_module.linkSystemLibrary("advapi32", .{});
dde_dll.root_module.linkSystemLibrary("userenv", .{});
dde_dll.root_module.linkSystemLibrary("ws2_32", .{});
//dde_dll.root_module.addObjectFile(.{ .path = tcl_source_folder ++ "/libtommath/win64/libtommath.dll.a" });
//dde_dll.root_module.addObjectFile(.{ .path = tcl_source_folder ++ "/compat/zlib/win64/libz.dll.a" });
dde_dll.root_module.addLibraryPath(b.path(tcl_source_folder ++ "/compat/zlib/win64"));
dde_dll.root_module.addLibraryPath(b.path(tcl_source_folder ++ "/libtommath/win64"));
//dde_dll.root_module.linkSystemLibrary("zlib1", .{});
//### JMN
dde_dll.root_module.linkLibrary(zlib_lib_compile);
dde_dll.root_module.linkSystemLibrary("tommath", .{});
dde_dll.root_module.link_libc = true;
//b.installArtifact(dde_dll);
install_dde_dll = b.addInstallArtifact(
dde_dll,
.{
.dest_dir = .{
.override = .{ .custom = "./lib/tcl" ++ tcl_dot_version ++ "/dde" },
},
},
);
//b.default_step.dependOn(&install_dde_dll.step);
}
//tcl9registry13.dll - tcl9
//tclregistry13.dll - tcl8
const reg_dll = b.addLibrary(.{
.linkage = .dynamic,
.name = std.fs.path.stem(reg_dll_file),
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
//reg_lib.step.dependOn(&stublib_archive.step);
reg_dll.step.dependOn(&finalstublib.step);
var install_reg_dll: *std.Build.Step.InstallArtifact = undefined;
if (target.result.os.tag == .windows) {
reg_dll.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic/"));
reg_dll.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win/"));
reg_dll.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/TclWinReg.c"),
.flags = dll_flags.items,
});
//LIBS
//reg_dll.root_module.addLibraryPath(stublib_archive.source.dirname());
reg_dll.root_module.addObjectFile(finalstublib.getEmittedBin());
//reg_dll.root_module.linkSystemLibrary("tclstub" ++ tcl_nodot_version); //libtclstubXX.a
//reg_dll.root_module.linkSystemLibrary("tclstub", .{}); //tcl9
reg_dll.root_module.linkSystemLibrary("netapi32", .{});
reg_dll.root_module.linkSystemLibrary("kernel32", .{});
reg_dll.root_module.linkSystemLibrary("user32", .{});
reg_dll.root_module.linkSystemLibrary("advapi32", .{});
reg_dll.root_module.linkSystemLibrary("userenv", .{});
reg_dll.root_module.linkSystemLibrary("ws2_32", .{});
//reg_dll.root_module.addObjectFile(.{ .path = tcl_source_folder ++ "/libtommath/win64/libtommath.dll.a" });
//reg_dll.root_module.addObjectFile(.{ .path = tcl_source_folder ++ "/compat/zlib/win64/libz.dll.a" });
reg_dll.root_module.addLibraryPath(b.path(tcl_source_folder ++ "/compat/zlib/win64"));
reg_dll.root_module.addLibraryPath(b.path(tcl_source_folder ++ "/libtommath/win64"));
//reg_dll.root_module.linkSystemLibrary("zlib1", .{});
//### JMN
reg_dll.root_module.linkLibrary(zlib_lib_compile);
reg_dll.root_module.linkSystemLibrary("tommath", .{});
reg_dll.root_module.link_libc = true;
//b.installArtifact(reg_dll);
//https://www.reddit.com/r/Zig/comments/1cxn08x/question_how_do_i_setup_a_custom_install_location/
install_reg_dll = b.addInstallArtifact(
reg_dll,
.{
.dest_dir = .{
.override = .{ .custom = "./lib/tcl" ++ tcl_dot_version ++ "/registry" },
},
},
);
//b.default_step.dependOn(&install_reg_dll.step);
}
//COREOBJS
var generic_sources = std.array_list.Managed([]const u8).init(b.allocator);
defer generic_sources.deinit();
try generic_sources.append(tcl_source_folder ++ "/generic/regcomp.c");
try generic_sources.append(tcl_source_folder ++ "/generic/regexec.c");
try generic_sources.append(tcl_source_folder ++ "/generic/regfree.c");
try generic_sources.append(tcl_source_folder ++ "/generic/regerror.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclAlloc.c");
//tcl9.0
try generic_sources.append(tcl_source_folder ++ "/generic/tclArithSeries.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclAssembly.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclAsync.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclBasic.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclBinary.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCkalloc.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclClock.c");
//tcl9.0
try generic_sources.append(tcl_source_folder ++ "/generic/tclClockFmt.c"); //tcl 9?
try generic_sources.append(tcl_source_folder ++ "/generic/tclCmdAH.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCmdIL.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCmdMZ.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCompCmds.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCompCmdsGR.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCompCmdsSZ.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCompExpr.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclCompile.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclConfig.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclDate.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclDictObj.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclDisassemble.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclEncoding.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclEnsemble.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclEnv.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclEvent.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclExecute.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclFCmd.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclFileName.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclGet.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclHash.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclHistory.c");
//tcl9.0
try generic_sources.append(tcl_source_folder ++ "/generic/tclIcu.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIndexObj.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclInterp.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIO.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIOCmd.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIOGT.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIORChan.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIORTrans.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIOSock.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclIOUtil.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclLink.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclLiteral.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclListObj.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclLoad.c");
//tclmainW mainw_obj
//try generic_sources.append(tcl_source_folder ++ "/generic/tclMainW.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclMain.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclNamesp.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclNotify.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOO.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOOBasic.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOOCall.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOODefineCmds.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOOInfo.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOOMethod.c");
//tcl9.0
try generic_sources.append(tcl_source_folder ++ "/generic/tclOOProp.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOOStubInit.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclObj.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclOptimize.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclPanic.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclParse.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclPathObj.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclPipe.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclPkg.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclPkgConfig.c"); //pkgconfig_obj
try generic_sources.append(tcl_source_folder ++ "/generic/tclPosixStr.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclPreserve.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclProc.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclProcess.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclRegexp.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclResolve.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclResult.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclScan.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclStringObj.c");
//tcl 9.0
try generic_sources.append(tcl_source_folder ++ "/generic/tclStrIdxTree.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclStrToD.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclStubInit.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclThread.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclThreadAlloc.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclThreadJoin.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclThreadStorage.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclTimer.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclTomMathInterface.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclTrace.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclUtf.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclUtil.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclVar.c");
try generic_sources.append(tcl_source_folder ++ "/generic/tclZipfs.c"); //zipfs_obj
try generic_sources.append(tcl_source_folder ++ "/generic/tclZlib.c");
//PLATFORMOBJS - win_source or unix_sources
var win_sources = std.array_list.Managed([]const u8).init(b.allocator);
defer win_sources.deinit();
var unix_sources = std.array_list.Managed([]const u8).init(b.allocator);
defer unix_sources.deinit();
switch (target.result.os.tag) {
.windows => {
try win_sources.append(tcl_source_folder ++ "/win/tclWin32Dll.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinChan.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinConsole.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinSerial.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinError.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinFCmd.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinFile.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinInit.c"); //wininit_obj
try win_sources.append(tcl_source_folder ++ "/win/tclWinLoad.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinNotify.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinPipe.c"); //winpipe_obj
try win_sources.append(tcl_source_folder ++ "/win/tclWinSock.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinThrd.c");
try win_sources.append(tcl_source_folder ++ "/win/tclWinTime.c");
},
else => {
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixChan.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixEvent.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixFCmd.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixFile.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixPipe.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixSock.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixTime.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixInit.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixThrd.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixCompat.c");
//NOTIFY_SRCS
try unix_sources.append(tcl_source_folder ++ "/unix/tclEpollNotfy.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclKqueueNotfy.c");
try unix_sources.append(tcl_source_folder ++ "/unix/tclSelectNotfy.c");
//try unix_sources.append(tcl_source_folder ++ "/unix/tclUnixNotfy.c");
switch (target.result.os.tag) {
.macos => {
try unix_sources.append(tcl_source_folder ++ "/unix/tclLoadDyld.c");
//MAC_OSX_SRCS
try unix_sources.append(tcl_source_folder ++ "/macosx/tclMacOSXBundle.c");
try unix_sources.append(tcl_source_folder ++ "/macosx/tclMacOSXFCmd.c");
try unix_sources.append(tcl_source_folder ++ "/macosx/tclMacOSXNotify.c");
},
else => {
try unix_sources.append(tcl_source_folder ++ "/unix/tclLoadDL.c");
},
}
},
}
var tommath_sources = std.array_list.Managed([]const u8).init(b.allocator);
//{
// //const subdir = "../libtommath";
// //const subdir = tcl_source_folder ++ "/libtommath";
// var dir = try fs.cwd().openDir(tommath_sourcedir, .{ .iterate = true });
// defer dir.close();
// var it = dir.iterate();
// const allowed_exts = [_][]const u8{
// ".c",
// };
// while (try it.next()) |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 tommath_sources.append(b.pathJoin(&.{
// tommath_sourcedir,
// entry.name,
// }));
// }
// }
// }
//}
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_add.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_add_d.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_and.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_clamp.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_clear.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_clear_multi.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_cmp.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_cmp_d.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_cmp_mag.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_cnt_lsb.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_copy.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_count_bits.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_div.c");
//tcl9.0
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_div_d.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_div_2.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_div_2d.c");
//tcl9.0
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_div_3.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_exch.c");
//tcl9.0
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_expt_n.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_get_mag_u64.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_grow.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_init.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_init_copy.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_init_i64.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_init_multi.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_init_set.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_init_size.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_init_u64.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_lshd.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_mod.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_mod_2d.c");
//tcl9.0
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_mul.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_mul_2.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_mul_2d.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_mul_d.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_neg.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_or.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_pack.c");
//tcl9.0
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_pack_count.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_radix_size.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_radix_smap.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_read_radix.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_rshd.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_set_i64.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_set_u64.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_shrink.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_sqr.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_sqrt.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_sub.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_sub_d.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_signed_rsh.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_to_ubin.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_to_radix.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_ubin_size.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_unpack.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_xor.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_mp_zero.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_add.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_balance_mul.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_karatsuba_mul.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_karatsuba_sqr.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_mul_digs.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_mul_digs_fast.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_reverse.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_sqr_fast.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_sqr.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_sub.c");
//tcl 9.0
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_toom_mul.c");
try tommath_sources.append(tommath_sourcedir ++ "/bn_s_mp_toom_sqr.c");
// larger set
var tcl_objs_static_sources = std.array_list.Managed([]const u8).init(b.allocator);
defer tcl_objs_static_sources.deinit();
try tcl_objs_static_sources.appendSlice(generic_sources.items);
if (target.result.os.tag == .windows) {
try tcl_objs_static_sources.appendSlice(win_sources.items);
} else {
try tcl_objs_static_sources.appendSlice(unix_sources.items);
}
try tcl_objs_static_sources.appendSlice(tommath_sources.items);
if (target.result.os.tag == .windows) {
var panic_dde_reg_sources = std.array_list.Managed([]const u8).init(b.allocator);
defer panic_dde_reg_sources.deinit();
try panic_dde_reg_sources.append(tcl_source_folder ++ "/win/tclWinPanic.c");
try panic_dde_reg_sources.append(tcl_source_folder ++ "/win/tclWinReg.c");
try panic_dde_reg_sources.append(tcl_source_folder ++ "/win/tclWinDde.c");
try tcl_objs_static_sources.appendSlice(panic_dde_reg_sources.items);
}
//smaller set
var tcl_objs_shared_sources = std.array_list.Managed([]const u8).init(b.allocator);
defer tcl_objs_shared_sources.deinit();
try tcl_objs_shared_sources.appendSlice(generic_sources.items);
if (target.result.os.tag == .windows) {
try tcl_objs_shared_sources.appendSlice(win_sources.items);
} else {
try tcl_objs_shared_sources.appendSlice(unix_sources.items);
}
//shouldn't need to be added ??
try tcl_objs_shared_sources.appendSlice(tommath_sources.items);
var tclobjs_flags = std.array_list.Managed([]const u8).init(b.allocator);
try tclobjs_flags.appendSlice(cc_flags.items);
try tclobjs_flags.appendSlice(&.{
"-DBUILD_tcl",
});
// ==============================================================================================================
const Flaginfo = struct { flags: std.array_list.Managed([]const u8) };
var build_specials = std.StringHashMap(Flaginfo).init(b.allocator);
defer build_specials.deinit();
// --------------------------------------------------------------------------------------------------------------
var mainw_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer mainw_flags.deinit();
try build_specials.put("tclWinPanic", .{ .flags = winpaniclib_flags });
try build_specials.put("tclWinReg", .{ .flags = winreg_obj_flags });
try build_specials.put("tclWinDde", .{ .flags = windde_obj_flags });
var appinit_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer appinit_flags.deinit();
try appinit_flags.appendSlice(cc_flags.items);
try appinit_flags.appendSlice(&.{
"-DUNICODE",
"-D_UNICODE",
});
try build_specials.put("tclAppInit", .{ .flags = appinit_flags });
try mainw_flags.appendSlice(cc_flags.items);
try mainw_flags.appendSlice(&.{
"-DBUILD_tcl",
"-DUNICODE",
"-D_UNICODE",
});
try build_specials.put("tclMain", .{ .flags = mainw_flags });
var wininit_obj_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer wininit_obj_flags.deinit();
try wininit_obj_flags.appendSlice(cc_flags.items);
try wininit_obj_flags.appendSlice(&.{
"-DBUILD_tcl",
});
try build_specials.put("tclWinInit", .{ .flags = wininit_obj_flags });
//try build_specials.put("tclPkgConfig",.{ .flags = config_flags});
try build_specials.put("tclPkgConfig", .{ .flags = cc_flags }); //cc_flags includes config_flags
var zipfs_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer zipfs_flags.deinit();
try zipfs_flags.appendSlice(cc_flags.items);
try zipfs_flags.appendSlice(&.{
"-DBUILD_tcl",
});
try build_specials.put("tclZipfs", .{ .flags = zipfs_flags });
//zipfs_obj.root_module.addIncludePath(.{ .path = tcl_source_folder ++ "/compat/zlib/contrib/minizip" });
var pipe_obj_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer pipe_obj_flags.deinit();
try pipe_obj_flags.appendSlice(cc_flags.items);
try pipe_obj_flags.appendSlice(&.{
"-DBUILD_tcl",
});
try build_specials.put("tclWinPipe", .{ .flags = pipe_obj_flags });
// ==============================================================================================================
const tcl_objs_static = b.addLibrary(.{
.linkage = .static,
.name = "tclobjs" ++ tcl_nodot_version,
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
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"));
} else {
tcl_objs_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
}
tcl_objs_static.root_module.addIncludePath(b.path(tommath_sourcedir));
tcl_objs_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib"));
tcl_objs_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/contrib/minizip")); //only required by tclZipfs - but ok for all?
//tclMain.c needs to be built twice on windows - with and without unicode flags.
//The UNICODE copy is already produced by the generic sources loop (build_specials
//maps tclMain -> mainw_flags); the ansi copy is added to the exe below. A second
//explicit UNICODE add here was harmless under zig 0.14.0-dev.2074 (the duplicate
//archive member was never extracted) but zig 0.14.1 links it -> duplicate symbols.
//if (target.result.os.tag == .windows) {
// tcl_objs_static.root_module.addCSourceFile(.{
// .file = b.path(tcl_source_folder ++ "/generic/tclMain.c"),
// .flags = mainw_flags.items,
// });
//}
var current_flagset: std.array_list.Managed([]const u8) = undefined;
for (tcl_objs_static_sources.items) |src| {
if (build_specials.get(std.fs.path.stem(src))) |val| {
//std.debug.print(">>> {s}\n", .{ std.fs.path.stem(src)});
current_flagset = val.flags;
} else {
//std.debug.print("--- {s}", .{ std.fs.path.stem(src)});
current_flagset = tclobjs_flags;
}
tcl_objs_static.root_module.addCSourceFile(.{
.file = b.path(src),
.flags = current_flagset.items,
});
}
tcl_objs_static.root_module.link_libc = true;
//b.installArtifact(tcl_objs);
//const tcl_objs_shared = b.addLibrary(.{
// .linkage = .static,
// .name = "tclobjsshared" ++ tcl_nodot_version,
// .root_module = b.createModule(.{ .target = target, .optimize = optimize }),
//});
//tcl_objs_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
//if (target.result.os.tag == .windows) {
// tcl_objs_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
//} else {
// tcl_objs_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
//}
//tcl_objs_shared.root_module.addIncludePath(b.path(tommath_sourcedir));
//tcl_objs_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib"));
//tcl_objs_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/contrib/minizip")); //only required by tclZipfs - but ok for all?
//if (target.result.os.tag == .windows) {
// tcl_objs_shared.root_module.addCSourceFile(.{
// .file = b.path(tcl_source_folder ++ "/generic/tclMain.c"),
// .flags = mainw_flags.items,
// });
//}
//var current_flagset_obj_shared: std.array_list.Managed([]const u8) = undefined;
//for (tcl_objs_shared_sources.items) |src| {
// if (build_specials.get(std.fs.path.stem(src))) |val| {
// //std.debug.print(">>> {s}\n", .{ std.fs.path.stem(src)});
// current_flagset_obj_shared = val.flags;
// } else {
// //std.debug.print("--- {s}", .{ std.fs.path.stem(src)});
// current_flagset_obj_shared = tclobjs_flags;
// }
// tcl_objs_shared.root_module.addCSourceFile(.{
// .file = b.path(src),
// .flags = current_flagset_obj_shared.items,
// });
//}
////tcl_objs_shared.root_module.addObjectFile(b.path(tcl_source_folder ++ "/libtommath/win64/libtommath.dll.a"));
//tcl_objs_shared.root_module.addObjectFile(b.path(tcl_source_folder ++ "/libtommath/win64/tommath.lib"));
////tcl_objs_shared.root_module.addObjectFile(b.path(tcl_source_folder ++ "/compat/zlib/win64/libz.dll.a"));
//tcl_objs_shared.root_module.addObjectFile(b.path(tcl_source_folder ++ "/compat/zlib/win64/zdll.lib"));
//tcl_objs_shared.root_module.link_libc = true;
//-- --- ---
//const tcl_lib_static = b.addLibrary(.{
// .linkage = .static,
// //libtcl90.dll.a ? - zig will append .lib or .a?
// .name = if (target.result.os.tag == .windows) "libtcl" ++ tcl_nodot_version ++ ".dll" else "libtcl" ++ tcl_nodot_version ++ ".so",
// .root_module = b.createModule(.{ .target = target, .optimize = optimize }),
//});
////jmn3 -test
//tcl_lib_static.step.dependOn(&zlib_lib_compile.step);
////tcl_lib_static.step.dependOn(&tommath_lib_compile.step);
//tcl_lib_static.step.dependOn(&wf_uuidh.step);
//tcl_lib_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/generic"));
//if (target.result.os.tag == .windows) {
// tcl_lib_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/win"));
//} else {
// tcl_lib_static.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
//}
//tcl_lib_static.root_module.addIncludePath(b.path(tommath_sourcedir));
//if (target.result.os.tag == .windows) {
// tcl_lib_static.root_module.addIncludePath(b.path(zlib_sourcedir));
// tcl_lib_static.root_module.addIncludePath(b.path(zlib_sourcedir ++ "/contrib/minizip")); //only required by tclZipfs - but ok for all?
//} else {
// //tcl_lib_static.root_module.addIncludePath(b.path("../zlib"));
// //tcl_lib_static.root_module.addIncludePath(b.path("../zlib/contrib/minizip"));
// tcl_lib_static.root_module.addIncludePath(b.path(zlib_sourcedir));
// tcl_lib_static.root_module.addIncludePath(b.path(zlib_sourcedir ++ "/contrib/minizip")); //only required by tclZipfs - but ok for all?
//}
//for (tcl_lib_sources.items) |src| {
// if (build_specials.get(std.fs.path.stem(src))) |val| {
// //std.debug.print(">>> {s}\n", .{ std.fs.path.stem(src)});
// current_flagset = val.flags;
// } else {
// //std.debug.print("--- {s}", .{ std.fs.path.stem(src)});
// current_flagset = tclobjs_flags;
// }
// tcl_lib_static.root_module.addCSourceFile(.{
// .file = b.path(src),
// .flags = current_flagset.items,
// });
//}
//jmn3
//tcl_lib_static.root_module.linkLibrary(zlib_lib_compile);
//tcl_lib_static.root_module.linkLibrary(tommath_lib_compile);
//tcl_lib_static.root_module.link_libc = true;
//b.installArtifact(tcl_lib_static);
// ==============================================================================================================
// ==============================================================================================================
//tclXX.dll or .so
var tcl_lib_shared_flags = std.array_list.Managed([]const u8).init(b.allocator);
try tcl_lib_shared_flags.appendSlice(base_flags.items);
try tcl_lib_shared_flags.appendSlice(&.{
"-pipe",
"-municode",
});
if (target.result.os.tag == .windows) {
try tcl_lib_shared_flags.appendSlice(&.{
"-static-libgcc",
});
//jmn3
try tcl_lib_shared_flags.appendSlice(&.{
"-Wl,-out-implib,tcl90.dll.a",
});
}
//try tcl_slib_flags.appendSlice(&.{
// "-DUNICODE",
// "-D_UNICODE",
//});
try tcl_lib_shared_flags.appendSlice(c_flags.items);
const tcl_lib_shared = b.addLibrary(.{
.linkage = .dynamic,
.name = "tcl" ++ tcl_nodot_version,
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
//tcl_lib_shared.verbose_link = true;
//tcl_lib_shared.setVerboseLink(true);
//tcl_lib_shared.step.dependOn(&tcl_objs_shared.step);
//jmn3 test
//if (target.result.os.tag == .linux) {
// 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"));
tcl_lib_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib"));
tcl_lib_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/contrib/minizip"));
} else {
tcl_lib_shared.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
tcl_lib_shared.root_module.addIncludePath(b.path(zlib_sourcedir));
tcl_lib_shared.root_module.addIncludePath(b.path(zlib_sourcedir ++ "/contrib/minizip")); //only required by tclZipfs - but ok for all?
}
tcl_lib_shared.root_module.addIncludePath(b.path(tommath_sourcedir));
// ---
// ?? needed for shared lib?
//if (target.result.os.tag == .windows) {
// tcl_lib_shared.root_module.addCSourceFile(.{
// .file = b.path(tcl_source_folder ++ "/generic/tclMain.c"),
// .flags = mainw_flags.items,
// });
//}
// ---
for (tcl_objs_shared_sources.items) |src| {
if (build_specials.get(std.fs.path.stem(src))) |val| {
//std.debug.print(">>> {s}\n", .{ std.fs.path.stem(src)});
current_flagset = val.flags;
} else {
//std.debug.print("--- {s}", .{ std.fs.path.stem(src)});
current_flagset = tclobjs_flags;
}
tcl_lib_shared.root_module.addCSourceFile(.{
.file = b.path(src),
.flags = current_flagset.items,
});
}
if (target.result.os.tag == .windows) {
tcl_lib_shared.root_module.addLibraryPath(b.path(tcl_source_folder ++ "/compat/zlib/win64"));
tcl_lib_shared.root_module.addLibraryPath(b.path(tcl_source_folder ++ "/libtommath/win64"));
}
if (target.result.os.tag == .windows) {
//tcl_lib_shared.root_module.addObjectFile(b.path(tcl_source_folder ++ "/libtommath/win64/libtommath.dll.a"));
//tcl_lib_shared.root_module.addObjectFile(b.path(tcl_source_folder ++ "/compat/zlib/win64/libz.dll.a"));
tcl_lib_shared.root_module.linkSystemLibrary("tommath", .{ .preferred_link_mode = .dynamic });
//tcl_lib_shared.root_module.linkSystemLibrary("zlib1", .{ .preferred_link_mode = .dynamic });
tcl_lib_shared.root_module.linkLibrary(zlib_lib_compile);
//tcl_lib_shared.root_module.linkSystemLibrary("tommath", .{});
//tcl_lib_shared.root_module.linkSystemLibrary("zlib1", .{});
} else {
//tcl_lib_shared.root_module.linkLibrary(tommath_lib_compile);
tcl_lib_shared.root_module.linkLibrary(zlib_lib_compile);
//tcl_lib_shared.root_module.linkSystemLibrary("zlib1", .{});
}
//JMN2024 install_path problem (sub_path is expected to be relative to the build root)
//tcl_lib_shared.root_module.addLibraryPath(b.path( b.pathJoin(&.{ b.install_path, "" }) ));
tcl_lib_shared.root_module.link_libc = true;
//SHLIB_LD_LIBS (LIBS)
if (target.result.os.tag == .windows) {
tcl_lib_shared.root_module.linkSystemLibrary("netapi32", .{});
tcl_lib_shared.root_module.linkSystemLibrary("kernel32", .{});
tcl_lib_shared.root_module.linkSystemLibrary("user32", .{});
tcl_lib_shared.root_module.linkSystemLibrary("advapi32", .{});
tcl_lib_shared.root_module.linkSystemLibrary("userenv", .{});
tcl_lib_shared.root_module.linkSystemLibrary("ws2_32", .{});
}
//tclshlib.root_module.linkSystemLibrary("libtommath", .{});
//tclshlib.root_module.linkSystemLibrary("zlib1", .{});
tcl_lib_shared.root_module.addWin32ResourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tcl.rc"),
.flags = &.{
"/I",
tcl_source_folder ++ "/generic",
},
});
b.installArtifact(tcl_lib_shared);
var ldflags_console = std.array_list.Managed([]const u8).init(b.allocator);
if (target.result.os.tag == .windows) {
try ldflags_console.appendSlice(&.{
"-mconsole",
"-static-libgcc",
"-Wl,--enable-auto-image-base",
"-Wl,--subsystem,console",
"-DTCL_BROKEN_MAINARGS",
});
}
try ldflags_console.appendSlice(&.{
//"-mwindows",
"-pipe",
"-municode",
});
//"-DTCL_MEM_DEBUG",
var tclsh_flags = std.array_list.Managed([]const u8).init(b.allocator);
try tclsh_flags.appendSlice(c_flags.items);
try tclsh_flags.appendSlice(appinit_flags.items);
try tclsh_flags.appendSlice(ldflags_console.items);
const tclsh_exe = b.addExecutable(.{
.name = tclsh_static,
//e.g tclsh90s (becomes tclsh90s.exe on windows)
//.root_source_file = .{ .path = appinit_path},
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
//9.0.5 sources dropped TCL_BROKEN_MAINARGS: tclAppInit.c now provides _tmain, which is
//wmain under -DUNICODE, so the mingw CRT must use the unicode entry (zig's -municode).
tclsh_exe.mingw_unicode_entry_point = true;
//tclsh_exe.setVerboseLink(true);
//tclsh_exe.link_gc_sections = false;
//tclsh_exe.want_lto = false;
tclsh_exe.step.dependOn(&finalstublib.step);
//tclsh.step.dependOn(&install_tcl_lib_file.step);
tclsh_exe.step.dependOn(&tcl_objs_static.step);
//tclsh_exe.step.dependOn(&tcl_lib_shared.step);
//tclsh_exe.step.dependOn(&tommath_lib_compile.step);
//JMN3 test
//if (target.result.os.tag == .linux) {
// 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"));
tclsh_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib"));
tclsh_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/contrib/minizip"));
} else {
tclsh_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
tclsh_exe.root_module.addIncludePath(b.path(zlib_sourcedir));
tclsh_exe.root_module.addIncludePath(b.path(zlib_sourcedir ++ "/contrib/minizip"));
}
tclsh_exe.root_module.addIncludePath(b.path(tommath_sourcedir));
//tclMain needs to be compiled twice on windows - with and without unicode
//we have already configured the one in the sources list to compile with mainw_flags (DUNICODE etc)
if (target.result.os.tag == .windows) {
//The ansi copy must NOT see UNICODE: undefine explicitly (the zig cc driver can
//supply windows-default defines - observed with zig 0.14.1 as duplicate
//Tcl_MainExW/common symbols vs the mainw copy) and use upstream's
//TCL_ASCII_MAIN guard so only the ascii Tcl_MainEx variant is emitted.
var mainansi_flags = std.array_list.Managed([]const u8).init(b.allocator);
defer mainansi_flags.deinit();
try mainansi_flags.appendSlice(tclobjs_flags.items);
try mainansi_flags.appendSlice(&.{
"-UUNICODE",
"-U_UNICODE",
"-DTCL_ASCII_MAIN",
});
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclMain.c"),
.flags = mainansi_flags.items,
});
}
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(appinit_path),
.flags = tclsh_flags.items,
});
// TCL_LIB_FILE libtclxx.dll.a (not the implib which is tclxx.dll.a)
// --
//tclsh_exe.root_module.addObjectFile(install_tcl_lib_file.source);
//tclsh_exe.root_module.addObjectFile(tcl_lib_static.getEmittedBin());
for (tcl_objs_static_sources.items) |src| {
if (build_specials.get(std.fs.path.stem(src))) |val| {
std.debug.print(">>> {s}\n", .{std.fs.path.stem(src)});
current_flagset = val.flags;
} else {
//std.debug.print("--- {s}", .{ std.fs.path.stem(src)});
current_flagset = tclobjs_flags;
}
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(src),
.flags = current_flagset.items,
});
}
//tclsh_exe.root_module.addObjectFile(finalstublib.getEmittedBin());
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLib.c"),
.flags = stublib_flags.items,
});
//experiment
//tclsh_exe.root_module.addCSourceFiles(.{
// .files = &.{
// "generic/tclStubCall.c",
// "tclStubLibTbl.c",
// },
// .flags = stublib_call_flags.items,
//});
//tcl9
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubCall.c"),
.flags = stublib_call_flags.items,
});
//tcl9
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLibTbl.c"),
.flags = stublib_tbl_flags.items,
});
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclTomMathStubLib.c"),
.flags = stublib_tom_flags.items,
});
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclOOStubLib.c"),
.flags = stublib_oo_flags.items,
});
if (target.result.os.tag == .windows) {
tclsh_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinPanic.c"),
.flags = winpaniclib_flags.items,
});
}
if (target.result.os.tag == .windows) {
tclsh_exe.root_module.linkSystemLibrary("netapi32", .{});
tclsh_exe.root_module.linkSystemLibrary("kernel32", .{});
tclsh_exe.root_module.linkSystemLibrary("user32", .{});
tclsh_exe.root_module.linkSystemLibrary("advapi32", .{});
tclsh_exe.root_module.linkSystemLibrary("userenv", .{});
tclsh_exe.root_module.linkSystemLibrary("ws2_32", .{});
}
if (target.result.os.tag == .windows) {
tclsh_exe.root_module.addObjectFile(b.path(tcl_source_folder ++ "/libtommath/win64/libtommath.dll.a"));
//### jmn
//tclsh_exe.root_module.addObjectFile(b.path(tcl_source_folder ++ "/compat/zlib/win64/libz.dll.a"));
} else {
//tclsh_exe.root_module.linkLibrary(tommath_lib_compile);
//tclsh_exe.root_module.linkLibrary(zlib_lib_compile);
}
//### jmn
tclsh_exe.step.dependOn(&zlib_lib_compile.step);
tclsh_exe.root_module.linkLibrary(zlib_lib_compile);
//tclsh.root_module.addLibraryPath(.{ .path = tcl_source_folder ++ "/compat/zlib/win64"});
//tclsh.root_module.addLibraryPath(.{ .path = tcl_source_folder ++ "/libtommath/win64"});
//tclsh.root_module.linkSystemLibrary("zlib1", .{});
//tclsh.root_module.linkSystemLibrary("tommath", .{});
//////////////////// tclvfs
//const tclvfs_compile = try tclvfs_static.build_tclvfs(tcl_source_folder, "../tclvfs", b, target, optimize, finalstublib);
//const build_tclvfs_step = b.step("build-tclvfs", "build tclvfs static library");
//build_tclvfs_step.dependOn(&tclvfs_compile.step);
//tclsh_exe.step.dependOn(&tclvfs_compile.step);
//tclsh_exe.root_module.linkLibrary(tclvfs_compile);
////////////////////
const tclvfs_compile = try tclvfs_shared.build_tclvfs(tcl_source_folder, "../tclvfs", b, target, optimize, finalstublib);
const build_tclvfs_step = b.step("build-tclvfs", "build tclvfs shared library");
build_tclvfs_step.dependOn(&tclvfs_compile.step);
tclsh_exe.step.dependOn(&tclvfs_compile.step); // review
if (target.result.os.tag == .windows) {
//G-102: vfs script package (lib/vfs1.4.2) installed by the recipe - the repl
//code interp wants vfs/vfs::zip (modpod mounting). Formerly suite.tcl post-build.
try tclvfs_shared.install_tclvfs_package("../tclvfs", b, tclvfs_compile);
}
////////////////////
//vqtcl/vlerq removed from the suite (user 2026-07-20): it was an attempt at old
//tclkit support and its source has no confirmed public upstream. When tclkit
//builds are investigated, TEMP_REFERENCE/metakit and TEMP_REFERENCE/KitCreator
//in the punkshell repo are the reference material.
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 = tclshrc_overlay,
.include_paths = &.{
b.path(tcl_source_folder ++ "/generic"),
b.path(tcl_source_folder ++ "/win"),
},
});
const install_tclsh = b.addInstallArtifact(tclsh_exe, .{});
std.debug.print("\n", .{});
std.debug.print("tclsh.kind {s}\n", .{@tagName(tclsh_exe.kind)});
std.debug.print("prefix {s}\n", .{b.install_path});
b.getInstallStep().dependOn(&install_tclsh.step);
//---------------------------------------------------------------------
//tclsh90spr: the TCLSH_PIPEREPL-patched shell (G-096). Identical to
//tclsh90s except tclMain.c is replaced by patches/tclMain_piperepl_905.c
//(rebased 2024 patch; gate default flipped per the G-103 punk-kit policy:
//enabled unless TCLSH_PIPEREPL=0 - see patches/README.md). Kept as a
//SEPARATE product so the stock shells stay byte-for-byte unpatched.
const tclsh_pr_exe = b.addExecutable(.{
.name = tclsh_static ++ "pr",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
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"));
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib"));
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/compat/zlib/contrib/minizip"));
} else {
tclsh_pr_exe.root_module.addIncludePath(b.path(tcl_source_folder ++ "/unix"));
tclsh_pr_exe.root_module.addIncludePath(b.path(zlib_sourcedir));
tclsh_pr_exe.root_module.addIncludePath(b.path(zlib_sourcedir ++ "/contrib/minizip"));
}
tclsh_pr_exe.root_module.addIncludePath(b.path(tommath_sourcedir));
if (target.result.os.tag == .windows) {
//ansi copy of the patched main (see the stock shell's mainansi note above)
var pr_mainansi_flags = std.array_list.Managed([]const u8).init(b.allocator);
try pr_mainansi_flags.appendSlice(tclobjs_flags.items);
try pr_mainansi_flags.appendSlice(&.{
"-UUNICODE",
"-U_UNICODE",
"-DTCL_ASCII_MAIN",
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path("patches/tclMain_piperepl_905.c"),
.flags = pr_mainansi_flags.items,
});
}
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(appinit_path),
.flags = tclsh_flags.items,
});
for (tcl_objs_static_sources.items) |src| {
if (build_specials.get(std.fs.path.stem(src))) |val| {
current_flagset = val.flags;
} else {
current_flagset = tclobjs_flags;
}
if (std.mem.eql(u8, std.fs.path.stem(src), "tclMain")) {
//the unicode copy: patched file in place of stock, same special flags
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path("patches/tclMain_piperepl_905.c"),
.flags = current_flagset.items,
});
} else {
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(src),
.flags = current_flagset.items,
});
}
}
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLib.c"),
.flags = stublib_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubCall.c"),
.flags = stublib_call_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclStubLibTbl.c"),
.flags = stublib_tbl_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclTomMathStubLib.c"),
.flags = stublib_tom_flags.items,
});
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/generic/tclOOStubLib.c"),
.flags = stublib_oo_flags.items,
});
if (target.result.os.tag == .windows) {
tclsh_pr_exe.root_module.addCSourceFile(.{
.file = b.path(tcl_source_folder ++ "/win/tclWinPanic.c"),
.flags = winpaniclib_flags.items,
});
tclsh_pr_exe.root_module.linkSystemLibrary("netapi32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("kernel32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("user32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("advapi32", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("userenv", .{});
tclsh_pr_exe.root_module.linkSystemLibrary("ws2_32", .{});
tclsh_pr_exe.root_module.addObjectFile(b.path(tcl_source_folder ++ "/libtommath/win64/libtommath.dll.a"));
}
tclsh_pr_exe.step.dependOn(&zlib_lib_compile.step);
tclsh_pr_exe.root_module.linkLibrary(zlib_lib_compile);
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 = 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, .{});
b.getInstallStep().dependOn(&install_tclsh_pr.step);
//---------------------------------------------------------------------
//OPTIONAL steps
const install_tzdata_step = b.step("install-tzdata", "install tcl timezone data");
//addInstallDirectory is recursive
const tzdata_install = b.addInstallDirectory(.{
.source_dir = b.path(tcl_source_folder ++ "/library/tzdata"),
.install_dir = .prefix,
.install_subdir = b.pathJoin(&.{ script_install_dir_rel, "tzdata" }),
});
install_tzdata_step.dependOn(&tzdata_install.step);
//****************************
//zig build install-libraries
const install_libraries = b.step("install-libraries", "install tcl libraries,tzdata,msgs (not required if zipfs being used)");
install_libraries.dependOn(&tclsh_exe.step);
if (target.result.os.tag == .windows) {
install_libraries.dependOn(&reg_dll.step);
install_libraries.dependOn(&dde_dll.step);
}
//Whole library tree -> lib/tcl9.0 (stock-install parity, G-098 finding: the
//library's own pkgIndex.tcl references subdir packages such as msgcat/ opt/
//cookiejar/ - installing only top-level .tcl files leaves phantom ifneeded
//entries pointing at files that were never installed).
const install_library_tree = b.addInstallDirectory(.{
.source_dir = b.path(tcl_source_folder ++ "/library"),
.install_dir = .prefix,
.install_subdir = "lib/tcl" ++ tcl_dot_version,
});
install_libraries.dependOn(&install_library_tree.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
const cookiejar_install = b.addInstallDirectory(.{
.source_dir = b.path(tcl_source_folder ++ "/library/cookiejar"),
.include_extensions = &.{ ".tcl", ".gz" },
.install_dir = .prefix,
//.install_subdir = b.pathJoin(&.{ script_install_dir_rel, "cookiejar0.2" }),
.install_subdir = b.pathJoin(&.{ script_install_dir_rel, "cookiejar" }),
});
install_libraries.dependOn(&cookiejar_install.step);
//(G-102: the former decorative 'echo --Installing package X--' banner steps were
//removed - 'echo' is a cmd builtin, and spawning it as a program only worked when
//a coreutils echo.exe happened to be on PATH; in a scrubbed environment the five
//banner spawns failed and took install-libraries down transitively.)
//tm modules under the module path (lib/tcl9/9.0). Version-stamped names follow
//the checkout's library tree; single source for the prefix install AND the
//G-103 family staging (a kit's tm root is /app/tcl9/<ver> - tm.tcl anchors at
//the dirname of the attached tcl_library).
const tm_installs = [_]struct { src: []const u8, tm: []const u8 }{
.{ .src = "/library/http/http.tcl", .tm = "http-2.10.2.tm" },
.{ .src = "/library/msgcat/msgcat.tcl", .tm = "msgcat-1.7.1.tm" },
.{ .src = "/library/tcltest/tcltest.tcl", .tm = "tcltest-2.5.11.tm" },
.{ .src = "/library/platform/platform.tcl", .tm = "platform-1.1.1.tm" },
.{ .src = "/library/platform/shell.tcl", .tm = "platform/shell-1.1.4.tm" },
};
inline for (tm_installs) |tmi| {
const tm_inst = b.addInstallFileWithDir(b.path(tcl_source_folder ++ tmi.src), .prefix, b.pathJoin(&.{ module_install_dir_rel, tcl_dot_version, tmi.tm }));
install_libraries.dependOn(&tm_inst.step);
}
//opt/*.tcl
const opt_tcl_install = b.addInstallDirectory(.{
.source_dir = b.path(tcl_source_folder ++ "/library/opt"),
.include_extensions = &.{".tcl"},
.install_dir = .prefix,
//.install_subdir = b.pathJoin(&.{ script_install_dir_rel, "opt0.4" }),
.install_subdir = b.pathJoin(&.{ script_install_dir_rel, "opt" }),
});
install_libraries.dependOn(&opt_tcl_install.step);
const opt_registry_install = b.addInstallDirectory(.{
.source_dir = b.path(tcl_source_folder ++ "/library/registry"),
.include_extensions = &.{".tcl"},
.install_dir = .prefix,
.install_subdir = b.pathJoin(&.{ script_install_dir_rel, "registry" }),
});
install_libraries.dependOn(&opt_registry_install.step);
const pkgindex_install = b.addInstallFile(b.path(tcl_source_folder ++ "/library/manifest.txt"), b.pathJoin(&.{ script_install_dir_rel, "pkgIndex.tcl" }));
install_libraries.dependOn(&pkgindex_install.step);
const encodings_install = b.addInstallDirectory(.{
.source_dir = b.path(tcl_source_folder ++ "/library/encoding"),
.include_extensions = &.{".enc"},
.install_dir = .prefix,
.install_subdir = b.pathJoin(&.{ script_install_dir_rel, "encoding" }),
});
install_libraries.dependOn(&encodings_install.step);
install_libraries.dependOn(install_tzdata_step);
if (target.result.os.tag == .windows) {
install_libraries.dependOn(&install_dde_dll.step);
//G-098: the registry install artifact existed but was hooked to nothing -
//library/registry/pkgIndex.tcl then pointed at a dll never installed
//(punkshell's punk module probes 'package require registry' at load).
install_libraries.dependOn(&install_reg_dll.step);
}
//****************************
//****************************
//zig build install-libraries
const make_vfs_root = b.addWriteFiles();
const tcl_library_path = make_vfs_root.addCopyDirectory(b.path(tcl_source_folder ++ "/library"), "base/tcl_library", .{});
//_ = make_vfs_root.addCopyDirectory(b.path(tcl_source_folder ++ "/library"), "tcl_library", .{});
_ = make_vfs_root.addCopyFile(b.path(tcl_source_folder ++ "/library/manifest.txt"), "base/tcl_library/pkgIndex.tcl");
_ = make_vfs_root.addCopyFile(b.path(tcl_source_folder ++ "/library/manifest.txt"), "base/tcl_library/pkgIndex.tcl");
if (target.result.os.tag == .windows) {
_ = make_vfs_root.addCopyFile(install_dde_dll.artifact.getEmittedBin(), "base/tcl_library/dde/" ++ dde_dll_file);
//_ = make_vfs_root.addCopyFile(dde_dll.getEmittedBin(), "tcl_library/dde/" ++ dde_dll_file);
_ = make_vfs_root.addCopyFile(reg_dll.getEmittedBin(), "base/tcl_library/registry/" ++ reg_dll_file);
make_vfs_root.step.dependOn(&reg_dll.step);
make_vfs_root.step.dependOn(&dde_dll.step);
}
//make_vfs_root.step.dependOn(&tclvfs_compile.step);
// The build_tclvfs_xxx.zig module is responsible for what files tclvfs needs to add the vfs root
// -pass it the *WriteFile we're building and let it add stuff
// *****************************
// tclvfs shared/static - todo
//_ = try tclvfs_static.vfsadd_tclvfs_files(tcl_source_folder, "../tclvfs", b, make_vfs_root);
_ = try tclvfs_shared.vfsadd_tclvfs_files(tcl_source_folder, "../tclvfs", b, make_vfs_root);
//hack
const tclvfs_info = tclvfs_shared.versionInfo(b, "../tclvfs");
_ = make_vfs_root.addCopyFile(tclvfs_compile.getEmittedBin(), b.fmt("base/lib/vfs{s}/{s}", .{ tclvfs_info.dotversion, tclvfs_info.dll_file }));
// *****************************
//vqtcl/vlerq zip payload removed with the rest of the vqtcl wiring (user 2026-07-20;
//old-tclkit experiment - see TEMP_REFERENCE/metakit + TEMP_REFERENCE/KitCreator).
//pass make_vfs_root to build_tclvfs
make_vfs_root.step.dependOn(install_libraries);
// ** system zip - problematic to call in the way we need from zig.build
// I know of no way to get the resulting zip file to strip the containing directory path when using addDirectoryArg.
// Normally this is done by passing a relative path as the input folder to be archived - but we need to use zig's addDirectoryArg instead of just addArg with a string
// If we don't use addDirectoryArg - the zig cache will return stale results despite the systemzip step depending on make_vfs_root.step, etc (why??)
// addDirectoryArg seems only able to provide a full path to the system command - not the relative path we would need to strip the containing folder.
// -j option of zip doesn't help as it strips all paths.
// This isn't a great concern here - as we have a partially built tclsh and we would prefer to use the less platform-dependant 'zipfs mkzip' functionality provided therein anyway
// But it would have been nice for debug/testing purposes to compare with a system zip
// zipfs mkzip may be fine for our purposes but doesn't give us some of the potential customisability of a system zip command
//
const systemzip = b.addSystemCommand(&.{ "zip", "-r" });
//systemzip.step.dependOn(&make_vfs_root.step);
systemzip.setCwd(make_vfs_root.getDirectory());
const out_zip = systemzip.addOutputFileArg("zipfs.zip");
//systemzip.addDirectoryArg(tcl_library_path);
systemzip.addArg("tcl_library");
const install_systemzip = b.addInstallFileWithDir(out_zip, .prefix, "zipfs.zip");
install_systemzip.step.dependOn(&systemzip.step);
//b.getInstallStep().dependOn(&install_systemzip.step);
// ** *** mkzip
var mkzip_run: *std.Build.Step.Run = undefined;
//mkzip_run.step.dependOn(&make_vfs_root.step); // ??????
//mkzip_run.step.dependOn(install_libraries);
if (target.result.os.tag == builtin.os.tag) {
mkzip_run = b.addRunArtifact(tclsh_exe);
} else {
mkzip_run = b.addSystemCommand(&.{"tclsh90"});
}
common.scrubTclEnv(mkzip_run);
mkzip_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
mkzip_run.addFileArg(b.path("tools/zipfs_mkzip.tcl"));
mkzip_run.addArg("-outfile");
const out_mkzip = mkzip_run.addOutputFileArg("mkzipfs.zip");
mkzip_run.addArg("-indir");
mkzip_run.addDirectoryArg(tcl_library_path.dirname());
mkzip_run.addArg("-strip");
mkzip_run.addDirectoryArg(tcl_library_path.dirname());
//mkzip_run.addArg(make_vfs_root.getDirectory());
const install_mkzip = b.addInstallFileWithDir(out_mkzip, .prefix, "mkzipfs.zip");
install_mkzip.step.dependOn(&mkzip_run.step);
const make_zipfs = b.step("make-zipfs", "make zipfs and attach to executable");
//make_zipfs.dependOn(&tclsh_exe.step);
make_zipfs.dependOn(&install_mkzip.step);
//make_zipfs.dependOn(&install_systemzip.step); //not really required
// ** ***
// ** *** *** mkimg
var mkimg_run: *std.Build.Step.Run = undefined;
if (target.result.os.tag == builtin.os.tag) {
mkimg_run = b.addRunArtifact(tclsh_exe);
} else {
//require an existing tclsh90 - with zipfs support on the path for x-builds
mkimg_run = b.addSystemCommand(&.{"tclsh90"});
}
common.scrubTclEnv(mkimg_run);
mkimg_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
mkimg_run.addFileArg(b.path("tools/zipfs_mkimg.tcl"));
mkimg_run.addArg("-outfile");
var out_mkimg: std.Build.LazyPath = undefined;
if (target.result.os.tag == .windows) {
out_mkimg = mkimg_run.addOutputFileArg("tclsh90szip.exe");
} else {
out_mkimg = mkimg_run.addOutputFileArg("tclsh90szip");
}
mkimg_run.addArg("-indir");
mkimg_run.addDirectoryArg(tcl_library_path.dirname());
mkimg_run.addArg("-strip");
mkimg_run.addDirectoryArg(tcl_library_path.dirname());
if (target.result.os.tag == .windows) {
//we can just leave off the -infile argument to use the calling interp as the prepended file
} else {
mkimg_run.addArg("-infile");
mkimg_run.addArtifactArg(tclsh_exe);
}
var install_mkimg: *std.Build.Step.InstallFile = undefined;
if (target.result.os.tag == .windows) {
install_mkimg = b.addInstallFileWithDir(out_mkimg, .prefix, "bin/tclsh90szip.exe");
} else {
install_mkimg = b.addInstallFileWithDir(out_mkimg, .prefix, "bin/tclsh90szip");
}
install_mkimg.step.dependOn(&mkimg_run.step);
make_zipfs.dependOn(&install_mkimg.step);
// ** *** ***
// ** *** *** mkimg for the piperepl shell -> tclsh90sprzip(.exe)
//Same attached-library wrap as tclsh90szip, but the prefix executable is the
//patched shell, named explicitly via -infile (the stock wrap uses the calling
//interp as its own prefix on windows).
var mkimg_pr_run: *std.Build.Step.Run = undefined;
if (target.result.os.tag == builtin.os.tag) {
mkimg_pr_run = b.addRunArtifact(tclsh_exe);
} else {
//require an existing tclsh90 - with zipfs support on the path for x-builds
mkimg_pr_run = b.addSystemCommand(&.{"tclsh90"});
}
common.scrubTclEnv(mkimg_pr_run);
mkimg_pr_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
mkimg_pr_run.addFileArg(b.path("tools/zipfs_mkimg.tcl"));
mkimg_pr_run.addArg("-outfile");
var out_mkimg_pr: std.Build.LazyPath = undefined;
if (target.result.os.tag == .windows) {
out_mkimg_pr = mkimg_pr_run.addOutputFileArg("tclsh90sprzip.exe");
} else {
out_mkimg_pr = mkimg_pr_run.addOutputFileArg("tclsh90sprzip");
}
mkimg_pr_run.addArg("-indir");
mkimg_pr_run.addDirectoryArg(tcl_library_path.dirname());
mkimg_pr_run.addArg("-strip");
mkimg_pr_run.addDirectoryArg(tcl_library_path.dirname());
mkimg_pr_run.addArg("-infile");
mkimg_pr_run.addArtifactArg(tclsh_pr_exe);
var install_mkimg_pr: *std.Build.Step.InstallFile = undefined;
if (target.result.os.tag == .windows) {
install_mkimg_pr = b.addInstallFileWithDir(out_mkimg_pr, .prefix, "bin/tclsh90sprzip.exe");
} else {
install_mkimg_pr = b.addInstallFileWithDir(out_mkimg_pr, .prefix, "bin/tclsh90sprzip");
}
install_mkimg_pr.step.dependOn(&mkimg_pr_run.step);
make_zipfs.dependOn(&install_mkimg_pr.step);
// ** *** ***
//****************************
const thread_build = try build_tclthread(tcl_source_folder, "../tclthread", b, target, optimize, finalstublib);
const build_tclthread_step = b.step("build-tclthread", "build tcl thread library");
build_tclthread_step.dependOn(&thread_build.lib.step);
b.getInstallStep().dependOn(build_tclthread_step);
//G-098: Tk loadable extension (windows target only for now). The build facts
//are hoisted (optional) so the G-103 bi-family staging can consume them.
var tk_build: ?TkBuild = null;
if (target.result.os.tag == .windows) {
tk_build = 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_build.?.lib.step);
}
//if !ZIPFS_BUILD
//install without zipfs
//b.getInstallStep().dependOn(install_libraries);
// ================== G-102 post-tclsh phases ==================
//Tcl phases executed by the FRESHLY BUILT suite shell as build steps - Tcl stays
//the tool exactly where a tclsh is guaranteed to exist because the suite just
//built it. Formerly suite.tcl's post-build blocks; suite.tcl now drives these
//via step names. Native-host builds only (the runs execute the built artifacts).
if (target.result.os.tag == builtin.os.tag) {
//-- smoke: shells respond with the expected patchlevel; the zip shell proves
//the attached library (tzdata + autoload)
const smoke_step = b.step("smoke", "smoke-test the built shells (patchlevel; zip: attached tzdata/autoload)");
const smoke_static = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(smoke_static);
smoke_static.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
smoke_static.has_side_effects = true;
smoke_static.addFileArg(b.path("tools/suite_smoke.tcl"));
smoke_static.addArgs(&.{ "-mode", "static", "-expect", tcl_h_patchlevel });
smoke_static.step.dependOn(b.getInstallStep());
smoke_step.dependOn(&smoke_static.step);
const zipexe_name = if (target.result.os.tag == .windows) tclsh_static ++ "zip.exe" else tclsh_static ++ "zip";
const smoke_zip = b.addSystemCommand(&.{b.pathJoin(&.{ b.install_path, "bin", zipexe_name })});
common.scrubTclEnv(smoke_zip);
smoke_zip.has_side_effects = true;
smoke_zip.addFileArg(b.path("tools/suite_smoke.tcl"));
smoke_zip.addArgs(&.{ "-mode", "zip", "-expect", tcl_h_patchlevel });
smoke_zip.step.dependOn(&install_mkimg.step);
smoke_step.dependOn(&smoke_zip.step);
//-- tklib: pure-tcl but its installer is NOT a straight copy (module
//exclusions + aggregating pkgIndex generation). Batch mode, packages only;
//the -pkg-path is a cached output dir installed into the prefix.
const tklib_ver = common.installerPackageVersion(b, "../tklib");
const tklib_step = b.step("tklib", "install tklib via its own installer under the suite-built shell (+Tk smoke)");
const tklib_run = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(tklib_run);
tklib_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
//cached (non-side-effect) Runs spawn with stdout IGNORED - on windows the
//child then has no stdout handle and tcl errors on any puts ('can not find
//channel named "stdout"', critcl's logging). Capturing gives it a real pipe.
_ = tklib_run.captureStdOut(.{});
tklib_run.setCwd(b.path("../tklib"));
tklib_run.addFileArg(b.path("../tklib/installer.tcl"));
tklib_run.addArgs(&.{ "-no-gui", "-no-wait", "-no-apps", "-no-html", "-no-nroff", "-no-examples", "-pkg-path" });
const tklib_out = tklib_run.addOutputDirectoryArg("tklib-pkg");
const tklib_install = b.addInstallDirectory(.{
.source_dir = tklib_out,
.install_dir = .prefix,
.install_subdir = b.fmt("lib/tklib{s}", .{tklib_ver}),
});
const tklib_smoke = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(tklib_smoke);
tklib_smoke.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
tklib_smoke.has_side_effects = true;
tklib_smoke.addFileArg(b.path("tools/pkg_smoke.tcl"));
tklib_smoke.addArgs(&.{ "-gui", "1", "tooltip" });
tklib_smoke.step.dependOn(&tklib_install.step);
tklib_smoke.step.dependOn(b.getInstallStep()); //Tk dll + pkgIndex + tcl library
tklib_step.dependOn(&tklib_smoke.step);
//-- tcllib: same installer family (critcl DSL stubbed to no-ops - this is the
//PURE-TCL side; the critcl-built tcllibc accelerators are the next step)
const tcllib_ver = common.installerPackageVersion(b, "../tcllib");
const tcllib_step = b.step("tcllib", "install tcllib (pure-tcl side) via its own installer under the suite-built shell (+smoke)");
const tcllib_run = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(tcllib_run);
tcllib_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
//cached (non-side-effect) Runs spawn with stdout IGNORED - on windows the
//child then has no stdout handle and tcl errors on any puts ('can not find
//channel named "stdout"', critcl's logging). Capturing gives it a real pipe.
_ = tcllib_run.captureStdOut(.{});
tcllib_run.setCwd(b.path("../tcllib"));
tcllib_run.addFileArg(b.path("../tcllib/installer.tcl"));
tcllib_run.addArgs(&.{ "-no-gui", "-no-wait", "-no-apps", "-no-html", "-no-nroff", "-no-examples", "-pkg-path" });
const tcllib_out = tcllib_run.addOutputDirectoryArg("tcllib-pkg");
const tcllib_install = b.addInstallDirectory(.{
.source_dir = tcllib_out,
.install_dir = .prefix,
.install_subdir = b.fmt("lib/tcllib{s}", .{tcllib_ver}),
});
const tcllib_smoke = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(tcllib_smoke);
tcllib_smoke.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
tcllib_smoke.has_side_effects = true;
tcllib_smoke.addFileArg(b.path("tools/pkg_smoke.tcl"));
tcllib_smoke.addArgs(&.{ "md5", "sha1", "cmdline" });
tcllib_smoke.step.dependOn(&tcllib_install.step);
tcllib_smoke.step.dependOn(b.getInstallStep());
tcllib_step.dependOn(&tcllib_smoke.step);
//-- tcllibc: tcllib's critcl accelerators, critcl driving zig cc (the
//tracked critcl_zig.config; zig resolved from PATH with our own dir
//prepended). sak.tcl's critcl target expects a tclkitsh on windows - this
//replicates its underlying invocation directly, as suite.tcl did.
const tcllibc_step = b.step("tcllibc", "build+install tcllibc (critcl accelerators, zig cc) under the suite-built shell (+accel smoke)");
const critcl_run = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(critcl_run);
critcl_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
//cached (non-side-effect) Runs spawn with stdout IGNORED - on windows the
//child then has no stdout handle and tcl errors on any puts ('can not find
//channel named "stdout"', critcl's logging). Capturing gives it a real pipe.
_ = critcl_run.captureStdOut(.{});
critcl_run.setCwd(b.path("../tcllib"));
critcl_run.addFileArg(b.path("../critcl/main.tcl"));
critcl_run.addArg("-config");
critcl_run.addFileArg(b.path("critcl_zig.config"));
//pin the build target to the config's explicit section: critcl otherwise
//GUESSES its target by probing PATH for compilers - with a gcc visible the
//config defaults engaged, but a scrubbed environment fell back to critcl's
//builtin msvc 'cl' target and never used the zig cc settings (G-102 finding).
critcl_run.addArgs(&.{ "-target", "win32-x86_64-zig" });
critcl_run.addArg("-cache");
_ = critcl_run.addOutputDirectoryArg("critcl-cache");
critcl_run.addArg("-force");
critcl_run.addArg("-libdir");
const critcl_libout = critcl_run.addOutputDirectoryArg("critcl-lib");
critcl_run.addArgs(&.{ "-pkg", "tcllibc" });
for (common.tcllibCritclFiles(b, "../tcllib")) |cf| {
critcl_run.addFileArg(b.path(b.pathJoin(&.{ "../tcllib/modules", cf })));
}
const zigdir = std.fs.path.dirname(b.graph.zig_exe) orelse ".";
const inherited_path = b.graph.environ_map.get("PATH") orelse "";
critcl_run.setEnvironmentVariable("PATH", b.fmt("{s}{c}{s}", .{ zigdir, std.fs.path.delimiter, inherited_path }));
const tcllibc_install = b.addInstallDirectory(.{
.source_dir = critcl_libout,
.install_dir = .prefix,
.install_subdir = "lib",
});
const tcllibc_smoke = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(tcllibc_smoke);
tcllibc_smoke.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
tcllibc_smoke.has_side_effects = true;
tcllibc_smoke.addFileArg(b.path("tools/pkg_smoke.tcl"));
tcllibc_smoke.addArgs(&.{ "-accel", "md5", "tcllibc" });
tcllibc_smoke.step.dependOn(&tcllibc_install.step);
tcllibc_smoke.step.dependOn(&tcllib_install.step); //accel host packages come from tcllib
tcllibc_smoke.step.dependOn(b.getInstallStep());
tcllibc_step.dependOn(&tcllibc_smoke.step);
const testreports_dir = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "testreports" }), "\\", "/");
// ================== G-103 runtime kit family ==================
//PLAIN (stock shell + batteries), PUNK (piperepl-patched shell, gate
//default-ON with TCLSH_PIPEREPL=0 opt-out, same batteries), PUNK-BI
//(punk + the libraries we build: Tk dll + script lib, tklib). Working
//names carry the dotted tcl patchlevel with punk-marked patched shells
//(tclsh9.0.5.exe / tclsh9.0.5-punk.exe / tclsh9.0.5-punk-bi.exe - G-103
//naming decision); -r<N> artifact copies are kit-family-artifacts' job.
//
//Attached-image layout (the make.tcl kit contract: what a future kit
//build extracts and merges under its .vfs payload):
// tcl_library/ core script library (the C-level zipfs boot looks only
// for /app/main.tcl and /app/tcl_library; no main.tcl -
// stock fallthrough to the interactive shell)
// tcl9/<ver>/ tm modules (tm.tcl anchors at dirname of tcl_library)
// lib/<pkg>/ batteries with their installed-shape pkgIndexes
// ($dir/../../bin dll references resolve to /app/bin)
// bin/<dll> battery dlls (loaded from zipfs via copy-to-temp -
// the characterized TIP-741 cleanup wart, goal Notes)
// lib/pkgIndex.tcl one-line stock hook joining lib/ to the package
// search: auto_path is [tcl_library, /app] and
// tclPkgUnknown re-scans auto_path growth mid-scan (the
// same mechanism tcllib's own top-level index uses)
if (target.result.os.tag == .windows) {
const family_step = b.step("kit-family", "assemble + verify the G-103 runtime kit family (plain/punk/punk-bi): self-contained batteries-attached shells -> <prefix>/family");
const family_libext_pkgindex =
\\#punkshell runtime kit family (G-103): stock package-index hook extending
\\#the package search into the attached image's lib/ tree (tclPkgUnknown
\\#re-scans auto_path growth mid-scan - the same mechanism tcllib's own
\\#top-level pkgIndex uses). The batteries keep their installed-shape
\\#indexes ($dir/../../bin dll references resolve to /app/bin).
\\if {$dir ni $::auto_path} {lappend ::auto_path $dir}
\\
;
const tkb = tk_build.?; //windows target (checked just above)
//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<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();
//core script library at the boot position (tzdata/encoding ride
//inside the source library tree), dde/registry dlls inside
const fam_tcl_library = famwf.addCopyDirectory(b.path(tcl_source_folder ++ "/library"), "base/tcl_library", .{});
_ = famwf.addCopyFile(b.path(tcl_source_folder ++ "/library/manifest.txt"), "base/tcl_library/pkgIndex.tcl");
_ = famwf.addCopyFile(install_dde_dll.artifact.getEmittedBin(), "base/tcl_library/dde/" ++ dde_dll_file);
_ = famwf.addCopyFile(reg_dll.getEmittedBin(), "base/tcl_library/registry/" ++ reg_dll_file);
//tm modules (same set the prefix install carries)
inline for (tm_installs) |tmi| {
_ = famwf.addCopyFile(b.path(tcl_source_folder ++ tmi.src), "base/tcl9/" ++ tcl_dot_version ++ "/" ++ tmi.tm);
}
//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 (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[ki] = fam_tcl_library.dirname();
}
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| {
const wrap = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(wrap);
wrap.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
wrap.addFileArg(b.path("tools/zipfs_mkimg.tcl"));
wrap.addArg("-outfile");
const wrapped = wrap.addOutputFileArg(fk.working_name);
wrap.addArg("-indir");
wrap.addDirectoryArg(family_tree_base[ki]);
wrap.addArg("-strip");
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}));
inst.step.dependOn(&wrap.step);
const installed_path = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "family", fk.working_name }), "\\", "/");
family_installed[ki] = installed_path;
//self-containment verification: the tool copies the kit alone into
//a scratch dir and probes from there with a scrubbed environment
//(no external Tcl visible - see tools/family_check.tcl)
const check = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(check);
check.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
check.has_side_effects = true;
check.addFileArg(b.path("tools/family_check.tcl"));
check.addArgs(&.{ "-exe", installed_path, "-variant", fk.variant, "-expectpatch", tcl_h_patchlevel });
check.addArgs(&.{ "-thread", thread_build.version, "-vfs", tclvfs_info.dotversion, "-tcllib", tcllib_ver });
if (std.mem.eql(u8, fk.variant, "punk-bi")) {
check.addArgs(&.{ "-tk", tkb.patchlevel, "-tklib", tklib_ver });
}
check.step.dependOn(&inst.step);
family_checks[ki] = &check.step;
family_step.dependOn(&check.step);
}
//-- kit-family-artifacts: punkbin-layout emission (-r<N> immutable
//artifact names + per-artifact toml metadata + sha1sums), run UNDER
//THE PLAIN FAMILY KIT itself - every emission doubles as a proof the
//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.
//(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<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 emit = b.addSystemCommand(&.{family_installed[0]});
common.scrubTclEnv(emit);
emit.has_side_effects = true;
emit.addFileArg(b.path("tools/family_artifacts.tcl"));
emit.addArgs(&.{ "-outdir", artifacts_outdir, "-rev", b.fmt("{d}", .{familyrev}), "-target", "win32-x86_64" });
emit.addArgs(&.{ "-suite", "suite_tcl90", "-tclpatch", tcl_h_patchlevel, "-zig", builtin.zig_version_string, "-optimize", @tagName(optimize) });
emit.addArgs(&.{ "-components", b.fmt("Thread {s} vfs {s} tcllib {s} tcllibc {s}", .{ thread_build.version, tclvfs_info.dotversion, tcllib_ver, tcllib_ver }) });
emit.addArgs(&.{ "-bicomponents", b.fmt("Tk {s} tklib {s}", .{ tkb.patchlevel, tklib_ver }) });
emit.addArgs(&.{ "-provenance", b.fmt("tcl {s} thread {s} tclvfs {s} tk {s} tcllib {s} tklib {s}", .{ uuid_tcl, uuid_thread, uuid_tclvfs, uuid_tk, uuid_tcllib, uuid_tklib }) });
emit.addArgs(&.{ "-testreports", testreports_dir });
emit.addArgs(&.{ "-kits", b.fmt("plain {{{s}}} punk {{{s}}} punk-bi {{{s}}}", .{ family_installed[0], family_installed[1], family_installed[2] }) });
for (family_checks) |cs| emit.step.dependOn(cs);
family_artifacts_step.dependOn(&emit.step);
}
// ==================
//-- test-gate: the core testsuite under the built shell, gated on parsed
//totals vs the tracked dispositioned baseline (all.tcl's exit code lies)
const gate_step = b.step("test-gate", "tcl core testsuite under the built shell, gated on parsed totals vs expected_test_failures.txt");
const testargs_opt = b.option([]const u8, "testargs", "extra tcltest args for test-gate (e.g -file http.test)") orelse "";
const gate_run = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(gate_run);
gate_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
gate_run.has_side_effects = true;
gate_run.addFileArg(b.path("tools/test_gate.tcl"));
gate_run.addArgs(&.{ "-library", "tclcore" });
gate_run.addArg("-testsdir");
gate_run.addArg(common.replaceAll(b, b.pathFromRoot(tcl_source_folder ++ "/tests"), "\\", "/"));
gate_run.addArg("-baseline");
gate_run.addFileArg(b.path("expected_test_failures.txt"));
gate_run.addArg("-logfile");
gate_run.addArg(common.replaceAll(b, b.pathFromRoot("../tcltest.log"), "\\", "/"));
gate_run.addArg("-summaryfile");
gate_run.addArg(b.fmt("{s}/tclcore.summary", .{testreports_dir}));
if (testargs_opt.len != 0) {
gate_run.addArgs(&.{ "-testargs", testargs_opt });
}
gate_run.step.dependOn(b.getInstallStep());
gate_step.dependOn(&gate_run.step);
//-- per-library testsuites (G-107): the external libraries the suite builds
//and ships run their own testsuites under the suite-built shell, with a
//per-library policy tier resolved at invocation (no recipe edits needed):
// gate parsed totals diffed against the tracked dispositioned baseline
// (core-gate semantics; default for the small load-bearing libs)
// record run must complete and emit report artifacts under
// <prefix>/testreports/ (evidence for the exact zig-built
// combinations nobody upstream tests); failures recorded, not fatal
// skip named step becomes a no-op (composite tuning)
//Override per-library: -Dtestpolicy-<lib>=gate|record|skip. Per-library
//driver/tcltest args: -Dtestargs-<lib>="..." (e.g '-notfile x.test' to
//disposition a problem file; recorded in the evidence summary).
//'test-libraries' runs the default set (thread tclvfs tcllib tklib). tk's
//full suite is deliberately OUTSIDE it: opt-in step 'test-tk' (needs an
//interactive desktop, maps real windows, runs tens of minutes; intended
//before PUBLISHING bi-family artifacts, not on every build).
//NOTE zig runs independent steps concurrently - for gate determinism of
//timing-sensitive suites drive combined test invocations with -j1
//(suite.tcl's test action does).
const LibSuite = struct {
name: []const u8, //step suffix, policy/testargs option suffix, report basename
testsdir: []const u8, //run cwd (from build root)
driver: []const u8, //driver script, relative to testsdir
default_policy: []const u8,
baseline: []const u8, //tracked baseline filename ("" = none tracked yet)
default_notfiles: []const u8, //default driver -notfile exclusions ("" = none)
deps: []const *std.Build.Step, //beyond the top-level install step
};
const thread_test_deps = [_]*std.Build.Step{install_libraries};
const tclvfs_test_deps = [_]*std.Build.Step{install_libraries};
const tcllib_test_deps = [_]*std.Build.Step{ install_libraries, &tcllib_install.step, &tcllibc_install.step };
const tklib_test_deps = [_]*std.Build.Step{ install_libraries, &tklib_install.step, &tcllib_install.step };
const tk_test_deps = [_]*std.Build.Step{install_libraries};
const lib_suites = [_]LibSuite{
//thread's testsuite requires the tree's exact version - the recipe derives
//the installed package version from the same tree (build_tclthread).
.{ .name = "thread", .testsdir = "../tclthread/tests", .driver = "all.tcl", .default_policy = "gate", .baseline = "expected_test_failures_thread.txt", .default_notfiles = "", .deps = &thread_test_deps },
.{ .name = "tclvfs", .testsdir = "../tclvfs/tests", .driver = "all.tcl", .default_policy = "gate", .baseline = "expected_test_failures_tclvfs.txt", .default_notfiles = "", .deps = &tclvfs_test_deps },
//tcllib via its own support/devel/all.tcl (sak's underlying driver -
//standard aggregate totals, per-file child interps). The installed
//tcllibc is on the shell's package path, so accelerator-aware suites
//engage the critcl accelerators ('E tcllibc <v>' markers in the log) -
//the single highest-value run here: zig-built accelerators against a
//static tclsh is a combination nobody upstream tests.
.{ .name = "tcllib", .testsdir = "../tcllib", .driver = "support/devel/all.tcl", .default_policy = "record", .baseline = "", .default_notfiles = "", .deps = &tcllib_test_deps },
.{ .name = "tklib", .testsdir = "../tklib", .driver = "support/devel/all.tcl", .default_policy = "record", .baseline = "", .default_notfiles = "", .deps = &tklib_test_deps },
//tk: the native-dialog suites (winDialog 72 cases, winMsgbox 19) are
//excluded by default - their message-injection automation does not
//drive the real OS dialogs under the batch/suite-built shell, so each
//case blocks waiting on a human (the census cannot complete unattended
//with them in). Re-include for a manual interactive run with
//-Dtestnotfiles-tk="" (then a human dismisses each dialog).
.{ .name = "tk", .testsdir = "../tk9/tests", .driver = "all.tcl", .default_policy = "record", .baseline = "", .default_notfiles = "winDialog.test winMsgbox.test", .deps = &tk_test_deps },
};
const libtests_step = b.step("test-libraries", "library testsuites under the built shell per policy tier (default: thread+tclvfs gate, tcllib+tklib record; tk excluded - see test-tk)");
for (lib_suites) |ls| {
const policy = b.option([]const u8, b.fmt("testpolicy-{s}", .{ls.name}), b.fmt("{s} testsuite policy: gate|record|skip (default {s})", .{ ls.name, ls.default_policy })) orelse ls.default_policy;
const lib_testargs = b.option([]const u8, b.fmt("testargs-{s}", .{ls.name}), b.fmt("extra tcltest/driver args for the {s} testsuite", .{ls.name})) orelse "";
const lib_notfiles = b.option([]const u8, b.fmt("testnotfiles-{s}", .{ls.name}), b.fmt("test-file glob patterns excluded from the {s} run (default \"{s}\")", .{ ls.name, ls.default_notfiles })) orelse ls.default_notfiles;
const is_tk = std.mem.eql(u8, ls.name, "tk");
const step = b.step(b.fmt("test-{s}", .{ls.name}), b.fmt("{s} testsuite under the built shell (policy: {s})", .{ ls.name, policy }));
if (!is_tk) libtests_step.dependOn(step);
if (std.mem.eql(u8, policy, "skip")) continue;
if (!std.mem.eql(u8, policy, "gate") and !std.mem.eql(u8, policy, "record")) {
std.debug.panic("-Dtestpolicy-{s}: unknown policy '{s}' (expected gate|record|skip)", .{ ls.name, policy });
}
const run = b.addRunArtifact(tclsh_exe);
common.scrubTclEnv(run);
run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src);
run.has_side_effects = true;
run.addFileArg(b.path("tools/test_gate.tcl"));
run.addArgs(&.{ "-library", ls.name, "-mode", policy });
run.addArg("-testsdir");
run.addArg(common.replaceAll(b, b.pathFromRoot(ls.testsdir), "\\", "/"));
run.addArgs(&.{ "-driver", ls.driver });
if (std.mem.eql(u8, policy, "gate")) {
run.addArg("-baseline");
if (ls.baseline.len != 0) {
run.addFileArg(b.path(ls.baseline));
} else {
//no tracked baseline yet: pass the conventional name so the
//engine's missing-baseline message names the file to create
run.addArg(common.replaceAll(b, b.pathFromRoot(b.fmt("expected_test_failures_{s}.txt", .{ls.name})), "\\", "/"));
}
}
run.addArg("-logfile");
run.addArg(b.fmt("{s}/{s}.log", .{ testreports_dir, ls.name }));
run.addArg("-summaryfile");
run.addArg(b.fmt("{s}/{s}.summary", .{ testreports_dir, ls.name }));
if (lib_testargs.len != 0) {
run.addArgs(&.{ "-testargs", lib_testargs });
}
if (lib_notfiles.len != 0) {
run.addArgs(&.{ "-notfiles", lib_notfiles });
}
run.step.dependOn(b.getInstallStep());
for (ls.deps) |dep| run.step.dependOn(dep);
step.dependOn(&run.step);
}
}
// ==================
}
//G-102 bootstrap mode: registered when this recipe is invoked from the TRACKED suite
//directory (see the mode note at the top of build()). The stage layout produced here
//is identical to suite.tcl's fossil staging, so both flows share the staged
//invocation - and the stage dir derives from THIS suite folder's name, preserving
//the copy-and-tweak isolation property (a copied suite stages into _build/<copy>).
fn bootstrapMode(b: *std.Build) !void {
//default ReleaseFast for suite.tcl parity (standardOptimizeOption would default
//Debug; the suite's runtimes are release artifacts)
const optimize = b.option(std.builtin.OptimizeMode, "optimize", "optimize mode forwarded to the staged build (default ReleaseFast)") orelse .ReleaseFast;
const suite_dirname = std.fs.path.basename(b.pathFromRoot(""));
const stage_rel = b.pathJoin(&.{ "..", "_build", suite_dirname });
const stage_tool = b.addExecutable(.{
.name = "stagetree",
.root_module = b.createModule(.{
.root_source_file = b.path("tools/stagetree.zig"),
.target = b.graph.host,
}),
});
const stage_step = b.step("stage", "materialize pinned sources (build.zig.zon) + recipe copy into ../_build/<suite> (existing fossil/git checkouts left untouched)");
//dependency name (build.zig.zon) -> stage dir (the recipe-coupled dir names
//documented in sources.config)
const source_dirs = [_][2][]const u8{
.{ "tcl", "tcl905" },
.{ "tclthread", "tclthread" },
.{ "tclvfs", "tclvfs" },
.{ "tk", "tk9" },
.{ "tklib", "tklib" },
.{ "tcllib", "tcllib" },
.{ "critcl", "critcl" },
};
var deps_pending = false;
for (source_dirs) |sd| {
const dep_name = sd[0];
const destdir = sd[1];
const dest_from_root = b.pathJoin(&.{ stage_rel, destdir });
//an existing fossil/git checkout is owned by the suite.tcl dev flow: skip the
//whole dependency so nothing is fetched for it (stagetree guards again at
//make time - this check just avoids needless fetches).
if (common.pathExists(b, b.pathJoin(&.{ dest_from_root, ".fslckout" })) or
common.pathExists(b, b.pathJoin(&.{ dest_from_root, "_FOSSIL_" })) or
common.pathExists(b, b.pathJoin(&.{ dest_from_root, ".git" })))
{
continue;
}
if (b.lazyDependency(dep_name, .{})) |dep| {
const run = b.addRunArtifact(stage_tool);
run.setName(b.fmt("stage {s}", .{destdir}));
run.has_side_effects = true;
//the dependency's content-addressed cache path doubles as the pin key:
//a manifest pin bump changes it, invalidating the materialized copy
const dep_root = dep.path("").getPath(b);
run.addArgs(&.{ "-key", dep_root, "-src", dep_root, "-dest", b.pathFromRoot(dest_from_root) });
stage_step.dependOn(&run.step);
} else {
deps_pending = true;
}
}
if (deps_pending) return; //zig fetches the lazy deps, then re-runs configure
//recipe copy -> <stage>/build (the same item list suite.tcl stages; per-item
//replace every run - recipe files are small tracked working files, no pin)
const recipe_items = [_][]const u8{
"build905.zig",
"build_common.zig",
"build_libtommath",
"build_zlib",
"build_tclvfs",
"build_tclthread",
"build_tk",
"critcl_zig.config",
"expected_test_failures.txt",
"expected_test_failures_thread.txt",
"expected_test_failures_tclvfs.txt",
"patches",
"src",
"tools",
};
for (recipe_items) |item| {
const run = b.addRunArtifact(stage_tool);
run.setName(b.fmt("stage recipe {s}", .{item}));
run.has_side_effects = true;
run.addArgs(&.{ "-key", "-", "-src", b.pathFromRoot(item), "-dest", b.pathFromRoot(b.pathJoin(&.{ stage_rel, "build", item })) });
stage_step.dependOn(&run.step);
}
//bootstrap: stage, then drive the staged recipe - the same nested invocation
//suite.tcl performs after its fossil staging. Per-zig-version cache dir (object
//caches must never be shared across zig versions - user-confirmed hazard), same
//name suite.tcl derives so both flows share the staged cache.
const steps_opt = b.option([]const []const u8, "steps", "steps for the staged build (repeat the flag; default: install install-libraries make-zipfs smoke tklib tcllib tcllibc kit-family kit-family-artifacts - the full pipeline short of test-gate)") orelse @as([]const []const u8, &.{ "install", "install-libraries", "make-zipfs", "smoke", "tklib", "tcllib", "tcllibc", "kit-family", "kit-family-artifacts" });
var cachever: []const u8 = builtin.zig_version_string;
cachever = common.replaceAll(b, cachever, "+", "_");
cachever = common.replaceAll(b, cachever, "/", "_");
cachever = common.replaceAll(b, cachever, ":", "_");
//forward-slash paths throughout (suite.tcl parity): the prefix flows into
//-D macros like TCL_PACKAGE_PATH as a C string literal - backslashes there are
//parsed as escape sequences and break the compile
const nested = b.addSystemCommand(&.{
b.graph.zig_exe,
"build",
"--build-file",
common.replaceAll(b, b.pathFromRoot(b.pathJoin(&.{ stage_rel, "build", "build905.zig" })), "\\", "/"),
"--cache-dir",
common.replaceAll(b, b.pathFromRoot(b.pathJoin(&.{ stage_rel, "build", b.fmt(".zig-cache-{s}", .{cachever}) })), "\\", "/"),
"--prefix",
common.replaceAll(b, b.pathFromRoot(b.pathJoin(&.{ stage_rel, "out" })), "\\", "/"),
b.fmt("-Doptimize={s}", .{@tagName(optimize)}),
});
for (steps_opt) |s| nested.addArg(s);
nested.setName("staged build (nested zig build)");
nested.has_side_effects = true;
//cwd = staged build dir (suite.tcl parity): parts of the recipe (e.g build_zlib's
//source iteration) resolve source paths relative to the process cwd, not the
//build root
nested.setCwd(.{ .cwd_relative = b.pathFromRoot(b.pathJoin(&.{ stage_rel, "build" })) });
//hermetic child shells (parity with suite.tcl): steps in the staged graph run
//the freshly built tclsh, which must not see the user's machine-level tcl env
for ([_][]const u8{ "TCLLIBPATH", "TCL_LIBRARY", "TK_LIBRARY" }) |ev| {
nested.removeEnvironmentVariable(ev);
}
nested.step.dependOn(stage_step);
const bootstrap_step = b.step("bootstrap", "stage + run the staged recipe end-to-end (no pre-existing tclsh required)");
bootstrap_step.dependOn(&nested.step);
}