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.
1413 lines
80 KiB
1413 lines
80 KiB
//build86.zig - suite_tcl86 zig recipe (G-099): Tcl core-8-6-branch windows |
|
//runtime from the pinned zig toolchain only, plus the core binary companions |
|
//punkshell's Tcl 8 targets need (thread 2.8, tclvfs trunk), gated against the |
|
//8.6 core testsuite. |
|
// |
|
//Forked from suite_tcl90/build905.zig (the shape reference; duplication-with-a- |
|
//note is the recorded posture) and DERIVED from the staged 8.6.18 |
|
//win/makefile.vc + rules.vc - the object inventory, per-object flag classes |
|
//(appcflags/pkgcflags/stubscflags), naming (SUFX rules) and install layout all |
|
//mirror that authority. The full derivation record lives in README.md |
|
//DERIVATION; key differences from the 9.0.5 recipe: |
|
// - NO zipfs in 8.6: no szip self-contained runtime, no zip staging steps. |
|
// The runtime is exe + dll + on-disk lib tree; hermetic resolution is |
|
// exe/dll-relative (../lib/tcl8.6 from bin/, via tclWinInit.c). |
|
// - zlib: 8.6's makefile.vc links a PREBUILT zdll.lib for shared builds and |
|
// compiles the 11-file ZLIBOBJS list only for static ones. This recipe |
|
// compiles that list into a static lib linked into BOTH the dll and the |
|
// static shell - no zlib1.dll is shipped (deviation recorded in README). |
|
// - entry points: gcc-class 8.6 builds define TCL_BROKEN_MAINARGS (win/ |
|
// tcl.m4:671 extra_cflags - carried globally here for parity): the CRT |
|
// entry is plain main() (console subsystem) and the real command line is |
|
// re-parsed from GetCommandLineW by tclAppInit.c's setargv. Without the |
|
// define tclAppInit provides only _tmain/wmain and mingw's fallback main |
|
// demands WinMain. No -municode / mingw_unicode_entry_point (unlike the |
|
// 9.0.5 recipe, whose sources dropped TCL_BROKEN_MAINARGS). |
|
// - tclMain.c is compiled twice BY UPSTREAM DESIGN (COREOBJS carries both |
|
// tclMain.obj and tclMainW.obj): the UNICODE copy provides Tcl_MainExW, |
|
// the plain copy Tcl_MainEx + the once-only support functions (8.6 guards |
|
// them with #ifndef UNICODE - no TCL_ASCII_MAIN needed). |
|
// - stub lib is 3 objects (tclStubLib, tclTomMathStubLib, tclOOStubLib - no |
|
// tclStubCall/tclStubLibTbl/tclWinPanic in 8.6); named libtclstub86. |
|
// - dde/registry: stubs-consumer dlls tcldde14.dll (1.4) / tclreg13.dll |
|
// (1.3) installed into the script library's dde/ and reg/ package dirs; |
|
// the STATIC shell instead compiles tclWinDde/tclWinReg in and registers |
|
// them via TCL_USE_STATIC_PACKAGES (makefile.vc static-build parity). |
|
// - tm modules install under lib/tcl8/{8.5,8.6}/ with versions parsed from |
|
// the library pkgIndex files (makefile.vc nmakehlp -V parity). |
|
// - MP_64BIT / MP_PREC / MP_FIXED_CUTOFFS are NOT defined (8.6's makefile.vc |
|
// defines none of them; the in-tree libtommath self-configures - all TUs |
|
// share one flag set so the mp_digit ABI stays consistent). |
|
// |
|
//Products (stock threaded-release naming - rules.vc keeps the 't' SUFX on 8.6): |
|
// bin/tcl86t.dll + tcl86t.lib shared core (+import lib for extensions) |
|
// bin/tclsh86t.exe dynamic shell linked against the dll |
|
// bin/tclsh86ts.exe static shell (kit-class; dde/registry static) |
|
// lib/libtclstub86.a stub lib for stubs-consumer extension builds |
|
// lib/tcl8.6/... script library tree (dde/reg dlls inside) |
|
// lib/tcl8/8.x/... tm modules; lib/thread2.8.13, lib/vfs1.4.2 |
|
// |
|
//Windows-only for the G-099 arc: cross-target parameterization is G-105's - |
|
//the single-target shape here deliberately avoids per-target tree copies so a |
|
//target dimension can be added without restructuring. |
|
const std = @import("std"); |
|
const builtin = @import("builtin"); |
|
const common = @import("build_common.zig"); |
|
const build_zlib86 = @import("build_zlib86/build_zlib86.zig").build_zlib86; |
|
const build_tclthread86 = @import("build_tclthread86/build_tclthread86.zig").build_tclthread86; |
|
const tclvfs86 = @import("build_tclvfs86/build_tclvfs86.zig"); |
|
const tk86 = @import("build_tk86/build_tk86.zig"); |
|
const mk4tcl86 = @import("build_mk4tcl86/build_mk4tcl86.zig"); |
|
|
|
const tcl_dot_version = "8.6"; |
|
const tcl_nodot_version = "86"; |
|
const tcl_source_folder = "../tcl86"; |
|
|
|
//stock makefile.vc/rules.vc naming for a threaded release build (SUFX 't' |
|
//kept while TCL_THREADS && TCL_VERSION <= 86; static adds 's') |
|
const tcl_dll_name = "tcl" ++ tcl_nodot_version ++ "t"; |
|
const tcl_dll_file = tcl_dll_name ++ ".dll"; |
|
const tclsh_dynamic = "tclsh" ++ tcl_nodot_version ++ "t"; |
|
const tclsh_static = "tclsh" ++ tcl_nodot_version ++ "ts"; |
|
const tcl_stub_lib_file = "libtclstub" ++ tcl_nodot_version ++ ".a"; |
|
const dde_dll_file = "tcldde14.dll"; //DDEDOTVERSION 1.4, SUFX 't' stripped for package dlls |
|
const reg_dll_file = "tclreg13.dll"; //REGDOTVERSION 1.3 |
|
|
|
comptime { |
|
//The suite pin (G-102 convention: the recipe records the zig version it is |
|
//written against). The recipe is 0.16-API; 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})); |
|
} |
|
} |
|
|
|
pub fn build(b: *std.Build) !void { |
|
//G-102 invocation modes (suite_tcl90 parity). STAGED mode: build root is the |
|
//staged recipe copy (_build/<suite>/build) with source trees beside it |
|
//(../tcl86 etc). BOOTSTRAP mode: build root is the TRACKED suite dir - only |
|
//staging steps register ('zig build stage|bootstrap', the no-tclsh entry). |
|
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."); |
|
} |
|
|
|
//PORTABILITY FLOOR (2026-08-06) - see the same pin in |
|
//../suite_tcl90/build905.zig for the full finding. Without an explicit |
|
//default_target, a flagless `zig build` resolves cpu_model by NATIVE |
|
//detection and ReleaseFast tunes the artifact to the build host; that |
|
//shipped the published tclsh9.0.5 family as AVX-512-only binaries that |
|
//die on #UD (0xC000001D, no output) on any other CPU. -Dcpu=native still |
|
//opts into a host-tuned local build; -Dtarget=<triple> is unaffected. |
|
const target = b.standardTargetOptions(.{ |
|
.default_target = .{ .cpu_model = .baseline }, |
|
}); |
|
const optimize = b.standardOptimizeOption(.{}); |
|
if (target.result.os.tag != .windows) { |
|
@panic("suite_tcl86 recipe is windows-only for the G-099 arc (a Tcl 8.6 WINDOWS runtime); cross-target parameterization is G-105's goal"); |
|
} |
|
|
|
const prefix = try std.fmt.allocPrint(b.allocator, "{s}", .{b.install_path}); |
|
const bindir = b.pathJoin(&.{ prefix, "bin" }); |
|
const libdir = b.pathJoin(&.{ prefix, "lib" }); |
|
const includedir = b.pathJoin(&.{ prefix, "include" }); |
|
const mandir = b.pathJoin(&.{ prefix, "man" }); |
|
const tcl_library = b.pathJoin(&.{ prefix, "lib", "tcl" ++ tcl_dot_version }); |
|
const script_install_dir_rel = "lib/tcl" ++ tcl_dot_version; |
|
const module_install_dir_rel = "lib/tcl8"; //tm root: tm.tcl scans lib/tcl8/8.4..8.6 |
|
std.debug.print("prefix {s}\n", .{prefix}); |
|
std.debug.print("tcl_library {s}\n", .{tcl_library}); |
|
|
|
//-- version facts from the staged tree (never hardcoded) |
|
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"); |
|
if (!std.mem.eql(u8, tcl_h_version, tcl_dot_version)) { |
|
std.debug.panic("staged tree is Tcl {s} but this recipe expresses Tcl " ++ tcl_dot_version ++ " (sources.config tcl ref / dir mismatch?)", .{tcl_h_version}); |
|
} |
|
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 }); |
|
|
|
//-- static zlib (see header: compiled into dll AND static shell) |
|
const zlib_lib_compile = try build_zlib86(tcl_source_folder ++ "/compat/zlib", b, target, optimize); |
|
|
|
//-- generated configure-products into the build cache (G-102 convention: |
|
//source trees are never written; delivery via overlay include dirs / overlay |
|
//rc copies). |
|
//tclUuid.h: 8.6.18's makefile.vc concatenates win/tclUuid.h.in + the |
|
//checkout's manifest.uuid - wrapfiletofile reproduces exactly that (the .in |
|
//is the one '#define TCL_VERSION_UUID \' line). tclEvent.c #includes 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 FIRST at each consumer so a stale tree-written copy can |
|
//never shadow the generated one; also carries the generation-step dependency. |
|
const tcluuid_include_dir = tcluuid_file1.dirname(); |
|
|
|
//tclsh.exe.manifest (tclsh.rc references it rc-file-relative, so the rc is |
|
//compiled from an overlay COPY beside the generated manifest). |
|
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"); |
|
|
|
//-- flag classes, mirroring rules.vc composition for a threaded release |
|
//build. OPTDEFINES equivalents translated for zig cc (clang/mingw-class): |
|
//HAVE_NO_SEH (no __try), HAVE_WSPIAPI_H (guard exists in tclWinPort.h), |
|
//header HAVEs clang satisfies; NO ZIPFS/MP defines (see header). |
|
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=\"\"", |
|
"-DSTDC_HEADERS=1", |
|
"-DHAVE_STDINT_H=1", |
|
"-DHAVE_INTTYPES_H=1", |
|
"-DHAVE_STDBOOL_H=1", |
|
"-DHAVE_STDIO_H=1", |
|
//8.6-era threading defines (rules.vc: TCL_THREADS always 1 on windows, |
|
//USE_THREAD_ALLOC rides it while TCL_VERSION < 87) |
|
"-DTCL_THREADS=1", |
|
"-DUSE_THREAD_ALLOC=1", |
|
"-DNDEBUG=1", |
|
"-DTCL_CFG_OPTIMIZED=1", |
|
"-DTCL_CFG_DO64BIT=1", |
|
"-DHAVE_NO_SEH=1", |
|
"-DHAVE_WSPIAPI_H=1", |
|
"-DHAVE_ZLIB=1", |
|
"-DMODULE_SCOPE=extern", |
|
//makefile.vc PRJ_DEFINES (rides every flag class via rules.vc): the |
|
//in-core libtommath uses FIXED cutoffs - TOMMATHOBJS carries no |
|
//bn_cutoffs.c, so without MP_FIXED_CUTOFFS the cutoff variables are |
|
//undefined at link. (inline=__inline, _CRT_*_NO_DEPRECATE and |
|
//z_off_t=__int64 there are MSVC-isms not translated for clang/mingw.) |
|
"-DMP_PREC=4", |
|
"-DMP_FIXED_CUTOFFS", |
|
//gcc-class entry handling (win/tcl.m4:671 extra_cflags): tclAppInit.c |
|
//provides plain main() + setargv re-parse only under this define - see |
|
//the entry-points note in the file header |
|
"-DTCL_BROKEN_MAINARGS", |
|
}); |
|
|
|
var config_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer config_flags.deinit(); |
|
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}), |
|
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}), |
|
//consumed by tclPkgConfig.c only when !STATIC_BUILD - safe globally |
|
"-DCFG_RUNTIME_DLLFILE=\"" ++ tcl_dll_file ++ "\"", |
|
}); |
|
|
|
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", |
|
"-fextended-identifiers", |
|
}); |
|
|
|
var c_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer c_flags.deinit(); |
|
try c_flags.appendSlice(&.{ |
|
"-O3", |
|
"-fomit-frame-pointer", |
|
"-finput-charset=UTF-8", |
|
//required on windows (suite_tcl90 finding: 'linsert {} 0 a a' class |
|
//crashes without both) - clang UBSan traps must stay fully off |
|
"-fno-sanitize=undefined", |
|
"-fno-sanitize-trap=undefined", |
|
}); |
|
|
|
//app-class flags (rules.vc appcflags: everything EXCEPT BUILD_tcl) |
|
var app_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer app_flags.deinit(); |
|
try app_flags.appendSlice(c_flags.items); |
|
try app_flags.appendSlice(warning_flags.items); |
|
try app_flags.appendSlice(config_flags.items); |
|
try app_flags.appendSlice(ac_flags.items); |
|
|
|
//pkg-class flags (rules.vc pkgcflags: + BUILD_tcl) - core objects, tommath |
|
var pkg_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer pkg_flags.deinit(); |
|
try pkg_flags.appendSlice(app_flags.items); |
|
try pkg_flags.appendSlice(&.{"-DBUILD_tcl"}); |
|
|
|
var pkg_static_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer pkg_static_flags.deinit(); |
|
try pkg_static_flags.appendSlice(pkg_flags.items); |
|
try pkg_static_flags.appendSlice(&.{"-DSTATIC_BUILD"}); |
|
|
|
//stubs-class flags (rules.vc stubscflags: STATIC_BUILD always, no BUILD_tcl, |
|
//no USE_TCL_STUBS while DOING_TCL) |
|
var stub_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer stub_flags.deinit(); |
|
try stub_flags.appendSlice(app_flags.items); |
|
try stub_flags.appendSlice(&.{ "-DSTATIC_BUILD", "-fno-lto" }); |
|
|
|
//tclMainW copies (makefile.vc: tclMain.c + /DUNICODE /D_UNICODE) |
|
var mainw_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer mainw_flags.deinit(); |
|
try mainw_flags.appendSlice(pkg_flags.items); |
|
try mainw_flags.appendSlice(&.{ "-DUNICODE", "-D_UNICODE" }); |
|
var mainw_static_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer mainw_static_flags.deinit(); |
|
try mainw_static_flags.appendSlice(pkg_static_flags.items); |
|
try mainw_static_flags.appendSlice(&.{ "-DUNICODE", "-D_UNICODE" }); |
|
|
|
//tclAppInit copies (makefile.vc: appcflags + UNICODE + TCL_USE_STATIC_PACKAGES) |
|
var appinit_dyn_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer appinit_dyn_flags.deinit(); |
|
try appinit_dyn_flags.appendSlice(app_flags.items); |
|
try appinit_dyn_flags.appendSlice(&.{ "-DUNICODE", "-D_UNICODE", "-DTCL_USE_STATIC_PACKAGES=0" }); |
|
var appinit_static_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer appinit_static_flags.deinit(); |
|
try appinit_static_flags.appendSlice(app_flags.items); |
|
try appinit_static_flags.appendSlice(&.{ "-DSTATIC_BUILD", "-DUNICODE", "-D_UNICODE", "-DTCL_USE_STATIC_PACKAGES=1" }); |
|
|
|
//stubs-consumer extension objects (makefile.vc: appcflags + USE_TCL_STUBS; |
|
//static-shell copies add STATIC_BUILD per OPTDEFINES) |
|
var stubext_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer stubext_flags.deinit(); |
|
try stubext_flags.appendSlice(app_flags.items); |
|
try stubext_flags.appendSlice(&.{"-DUSE_TCL_STUBS=1"}); |
|
var stubext_static_flags = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer stubext_static_flags.deinit(); |
|
try stubext_static_flags.appendSlice(app_flags.items); |
|
try stubext_static_flags.appendSlice(&.{ "-DSTATIC_BUILD", "-DUSE_TCL_STUBS=1" }); |
|
|
|
//-- source inventory (win/makefile.vc COREOBJS/PLATFORMOBJS/TOMMATHOBJS, |
|
//8.6.18 staged tree). tclMain.c appears ONCE here - the per-consumer loops |
|
//compile it with mainw flags (the tclMainW.obj copy) and each consumer adds |
|
//the plain ansi copy explicitly (both objs are in stock COREOBJS). |
|
var generic_sources = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer generic_sources.deinit(); |
|
const generic_names = [_][]const u8{ |
|
"regcomp", "regerror", "regexec", "regfree", |
|
"tclAlloc", "tclAssembly", "tclAsync", "tclBasic", |
|
"tclBinary", "tclCkalloc", "tclClock", "tclCmdAH", |
|
"tclCmdIL", "tclCmdMZ", "tclCompCmds", "tclCompCmdsGR", |
|
"tclCompCmdsSZ", "tclCompExpr", "tclCompile", "tclConfig", |
|
"tclDate", "tclDictObj", "tclDisassemble", "tclEncoding", |
|
"tclEnsemble", "tclEnv", "tclEvent", "tclExecute", |
|
"tclFCmd", "tclFileName", "tclGet", "tclHash", |
|
"tclHistory", "tclIndexObj", "tclInterp", "tclIO", |
|
"tclIOCmd", "tclIOGT", "tclIOSock", "tclIOUtil", |
|
"tclIORChan", "tclIORTrans", "tclLink", "tclListObj", |
|
"tclLiteral", "tclLoad", "tclMain", "tclNamesp", |
|
"tclNotify", "tclOO", "tclOOBasic", "tclOOCall", |
|
"tclOODefineCmds", "tclOOInfo", "tclOOMethod", "tclOOStubInit", |
|
"tclObj", "tclOptimize", "tclPanic", "tclParse", |
|
"tclPathObj", "tclPipe", "tclPkg", "tclPkgConfig", |
|
"tclPosixStr", "tclPreserve", "tclProc", "tclRegexp", |
|
"tclResolve", "tclResult", "tclScan", "tclStringObj", |
|
"tclStrToD", "tclStubInit", "tclThread", "tclThreadAlloc", |
|
"tclThreadJoin", "tclThreadStorage", "tclTimer", "tclTomMathInterface", |
|
"tclTrace", "tclUtf", "tclUtil", "tclVar", |
|
"tclZlib", |
|
}; |
|
inline for (generic_names) |nm| { |
|
try generic_sources.append(tcl_source_folder ++ "/generic/" ++ nm ++ ".c"); |
|
} |
|
|
|
var win_sources = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer win_sources.deinit(); |
|
const win_names = [_][]const u8{ |
|
"tclWin32Dll", "tclWinChan", "tclWinConsole", "tclWinError", |
|
"tclWinFCmd", "tclWinFile", "tclWinInit", "tclWinLoad", |
|
"tclWinNotify", "tclWinPipe", "tclWinSerial", "tclWinSock", |
|
"tclWinThrd", "tclWinTime", |
|
}; |
|
inline for (win_names) |nm| { |
|
try win_sources.append(tcl_source_folder ++ "/win/" ++ nm ++ ".c"); |
|
} |
|
|
|
var tommath_sources = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer tommath_sources.deinit(); |
|
const tommath_names = [_][]const u8{ |
|
"bn_mp_add", "bn_mp_add_d", "bn_mp_and", "bn_mp_clamp", |
|
"bn_mp_clear", "bn_mp_clear_multi", "bn_mp_cmp", "bn_mp_cmp_d", |
|
"bn_mp_cmp_mag", "bn_mp_cnt_lsb", "bn_mp_copy", "bn_mp_count_bits", |
|
"bn_mp_div", "bn_mp_div_d", "bn_mp_div_2", "bn_mp_div_2d", |
|
"bn_s_mp_div_3", "bn_mp_exch", "bn_mp_expt_n", "bn_mp_grow", |
|
"bn_mp_init", "bn_mp_init_copy", "bn_mp_init_multi", "bn_mp_init_set", |
|
"bn_mp_init_size", "bn_mp_lshd", "bn_mp_mod", "bn_mp_mod_2d", |
|
"bn_mp_mul", "bn_mp_mul_2", "bn_mp_mul_2d", "bn_mp_mul_d", |
|
"bn_mp_neg", "bn_mp_or", "bn_mp_pack", "bn_mp_pack_count", |
|
"bn_mp_radix_size", "bn_mp_radix_smap", "bn_mp_read_radix", "bn_mp_rshd", |
|
"bn_mp_set", "bn_mp_shrink", "bn_mp_sqr", "bn_mp_sqrt", |
|
"bn_mp_sub", "bn_mp_sub_d", "bn_mp_signed_rsh", "bn_mp_to_ubin", |
|
"bn_mp_to_radix", "bn_mp_ubin_size", "bn_mp_unpack", "bn_mp_xor", |
|
"bn_mp_zero", "bn_s_mp_add", "bn_s_mp_balance_mul", "bn_s_mp_karatsuba_mul", |
|
"bn_s_mp_karatsuba_sqr", "bn_s_mp_mul_digs", "bn_s_mp_mul_digs_fast", "bn_s_mp_reverse", |
|
"bn_s_mp_sqr", "bn_s_mp_sqr_fast", "bn_s_mp_sub", "bn_s_mp_toom_sqr", |
|
"bn_s_mp_toom_mul", |
|
}; |
|
inline for (tommath_names) |nm| { |
|
try tommath_sources.append(tcl_source_folder ++ "/libtommath/" ++ nm ++ ".c"); |
|
} |
|
|
|
var core_sources = std.array_list.Managed([]const u8).init(b.allocator); |
|
defer core_sources.deinit(); |
|
try core_sources.appendSlice(generic_sources.items); |
|
try core_sources.appendSlice(win_sources.items); |
|
try core_sources.appendSlice(tommath_sources.items); |
|
|
|
const core_include = struct { |
|
fn apply(m: *std.Build.Module, bb: *std.Build, uuid_dir: std.Build.LazyPath) void { |
|
m.addIncludePath(uuid_dir); //overlay first (G-102: stale tree copies can never shadow) |
|
m.addIncludePath(bb.path(tcl_source_folder ++ "/generic")); |
|
m.addIncludePath(bb.path(tcl_source_folder ++ "/win")); |
|
m.addIncludePath(bb.path(tcl_source_folder ++ "/libtommath")); |
|
m.addIncludePath(bb.path(tcl_source_folder ++ "/compat/zlib")); |
|
} |
|
}.apply; |
|
const win_syslibs = struct { |
|
fn apply(m: *std.Build.Module) void { |
|
//rules.vc baselibs |
|
m.linkSystemLibrary("netapi32", .{}); |
|
m.linkSystemLibrary("kernel32", .{}); |
|
m.linkSystemLibrary("user32", .{}); |
|
m.linkSystemLibrary("advapi32", .{}); |
|
m.linkSystemLibrary("userenv", .{}); |
|
m.linkSystemLibrary("ws2_32", .{}); |
|
} |
|
}.apply; |
|
|
|
// ================================================================ |
|
//stub lib: libtclstub86 (TCLSTUBOBJS = tclStubLib, tclTomMathStubLib, |
|
//tclOOStubLib - the 8.6 set) |
|
const finalstublib = b.addLibrary(.{ |
|
.linkage = .static, |
|
.name = "libtclstub" ++ tcl_nodot_version, |
|
.root_module = b.createModule(.{ |
|
.target = target, |
|
.optimize = optimize, |
|
}), |
|
}); |
|
core_include(finalstublib.root_module, b, tcluuid_include_dir); |
|
inline for (.{ "tclStubLib", "tclTomMathStubLib", "tclOOStubLib" }) |nm| { |
|
finalstublib.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/generic/" ++ nm ++ ".c"), |
|
.flags = stub_flags.items, |
|
}); |
|
} |
|
finalstublib.root_module.link_libc = true; |
|
|
|
//ar'd copy under the mingw-conventional archive name -> lib/libtclstub86.a |
|
//(the extension-builder consumable; the s flag is ranlib-equivalent) |
|
const wf_stub = b.addWriteFiles(); |
|
const lz_stublib_file = wf_stub.addCopyFile(finalstublib.getEmittedBin(), b.fmt("buildarchive/{s}", .{finalstublib.out_filename})); |
|
const create_stublib_archive = b.addSystemCommand(&.{ b.graph.zig_exe, "ar", "crs", "--" }); |
|
const stublib_archive_file = create_stublib_archive.addOutputFileArg("buildarchive/" ++ tcl_stub_lib_file); |
|
create_stublib_archive.addFileArg(lz_stublib_file); |
|
const installed_stublib_archive = b.addInstallFileWithDir(stublib_archive_file, .prefix, "lib/" ++ tcl_stub_lib_file); |
|
installed_stublib_archive.step.dependOn(&create_stublib_archive.step); |
|
b.getInstallStep().dependOn(&installed_stublib_archive.step); |
|
|
|
// ================================================================ |
|
//tcl86t.dll - the shared core (zlib compiled in statically; see header) |
|
const tcl_lib_shared = b.addLibrary(.{ |
|
.linkage = .dynamic, |
|
.name = tcl_dll_name, |
|
.root_module = b.createModule(.{ |
|
.target = target, |
|
.optimize = optimize, |
|
}), |
|
}); |
|
core_include(tcl_lib_shared.root_module, b, tcluuid_include_dir); |
|
for (core_sources.items) |src| { |
|
const flags = if (std.mem.eql(u8, std.fs.path.stem(src), "tclMain")) mainw_flags.items else pkg_flags.items; |
|
tcl_lib_shared.root_module.addCSourceFile(.{ .file = b.path(src), .flags = flags }); |
|
} |
|
//the plain (ansi) tclMain copy - Tcl_MainEx + the #ifndef UNICODE support fns |
|
tcl_lib_shared.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/generic/tclMain.c"), |
|
.flags = pkg_flags.items, |
|
}); |
|
tcl_lib_shared.root_module.linkLibrary(zlib_lib_compile); |
|
win_syslibs(tcl_lib_shared.root_module); |
|
tcl_lib_shared.root_module.link_libc = true; |
|
tcl_lib_shared.root_module.addWin32ResourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tcl.rc"), |
|
.include_paths = &.{ |
|
b.path(tcl_source_folder ++ "/generic"), |
|
b.path(tcl_source_folder ++ "/win"), |
|
}, |
|
}); |
|
b.installArtifact(tcl_lib_shared); //bin/tcl86t.dll (+ implib -> lib/) |
|
|
|
// ================================================================ |
|
//tclsh86ts.exe - the static shell (kit-class). STATIC_BUILD everywhere, |
|
//tclWinReg/tclWinDde compiled in and registered via TCL_USE_STATIC_PACKAGES |
|
//(makefile.vc static-build PLATFORMOBJS parity), stub objs linked (the nmake |
|
//TCLSH link includes TCLSTUBLIB), entry = plain main (TCL_BROKEN_MAINARGS). |
|
const tclsh_static_exe = b.addExecutable(.{ |
|
.name = tclsh_static, |
|
.root_module = b.createModule(.{ |
|
.root_source_file = b.path("src/main.zig"), |
|
.target = target, |
|
.optimize = optimize, |
|
}), |
|
}); |
|
tclsh_static_exe.stack_size = 2300000; //makefile.vc CONEXECMD -stack:2300000 |
|
//console subsystem, C-provided main(): without this zig infers a windows- |
|
//subsystem entry for the zig-root exe and the link demands WinMain |
|
tclsh_static_exe.subsystem = .Console; |
|
core_include(tclsh_static_exe.root_module, b, tcluuid_include_dir); |
|
for (core_sources.items) |src| { |
|
const flags = if (std.mem.eql(u8, std.fs.path.stem(src), "tclMain")) mainw_static_flags.items else pkg_static_flags.items; |
|
tclsh_static_exe.root_module.addCSourceFile(.{ .file = b.path(src), .flags = flags }); |
|
} |
|
tclsh_static_exe.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/generic/tclMain.c"), |
|
.flags = pkg_static_flags.items, |
|
}); |
|
//static-build PLATFORMOBJS additions (registered as static packages) |
|
tclsh_static_exe.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclWinReg.c"), |
|
.flags = stubext_static_flags.items, |
|
}); |
|
tclsh_static_exe.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclWinDde.c"), |
|
.flags = stubext_static_flags.items, |
|
}); |
|
tclsh_static_exe.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclAppInit.c"), |
|
.flags = appinit_static_flags.items, |
|
}); |
|
inline for (.{ "tclStubLib", "tclTomMathStubLib", "tclOOStubLib" }) |nm| { |
|
tclsh_static_exe.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/generic/" ++ nm ++ ".c"), |
|
.flags = stub_flags.items, |
|
}); |
|
} |
|
tclsh_static_exe.root_module.linkLibrary(zlib_lib_compile); |
|
win_syslibs(tclsh_static_exe.root_module); |
|
tclsh_static_exe.root_module.link_libc = true; |
|
tclsh_static_exe.root_module.addWin32ResourceFile(.{ |
|
.file = tclshrc_overlay, |
|
.include_paths = &.{ |
|
b.path(tcl_source_folder ++ "/generic"), |
|
b.path(tcl_source_folder ++ "/win"), |
|
}, |
|
}); |
|
const install_tclsh_static = b.addInstallArtifact(tclsh_static_exe, .{}); |
|
b.getInstallStep().dependOn(&install_tclsh_static.step); |
|
|
|
// ================================================================ |
|
//tclsh86t.exe - the dynamic shell against tcl86t.dll (stock pair; proves |
|
//the dll deliverable works) |
|
const tclsh_dyn_exe = b.addExecutable(.{ |
|
.name = tclsh_dynamic, |
|
.root_module = b.createModule(.{ |
|
.root_source_file = b.path("src/main.zig"), |
|
.target = target, |
|
.optimize = optimize, |
|
}), |
|
}); |
|
tclsh_dyn_exe.stack_size = 2300000; |
|
tclsh_dyn_exe.subsystem = .Console; //see the static shell's subsystem note |
|
core_include(tclsh_dyn_exe.root_module, b, tcluuid_include_dir); |
|
tclsh_dyn_exe.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclAppInit.c"), |
|
.flags = appinit_dyn_flags.items, |
|
}); |
|
tclsh_dyn_exe.root_module.linkLibrary(tcl_lib_shared); |
|
win_syslibs(tclsh_dyn_exe.root_module); |
|
tclsh_dyn_exe.root_module.link_libc = true; |
|
tclsh_dyn_exe.root_module.addWin32ResourceFile(.{ |
|
.file = tclshrc_overlay, |
|
.include_paths = &.{ |
|
b.path(tcl_source_folder ++ "/generic"), |
|
b.path(tcl_source_folder ++ "/win"), |
|
}, |
|
}); |
|
const install_tclsh_dyn = b.addInstallArtifact(tclsh_dyn_exe, .{}); |
|
b.getInstallStep().dependOn(&install_tclsh_dyn.step); |
|
|
|
// ================================================================ |
|
//dde/registry loadable packages (stubs consumers; installed into the script |
|
//library's dde/ and reg/ dirs where library pkgIndexes expect them) |
|
const dde_dll = b.addLibrary(.{ |
|
.linkage = .dynamic, |
|
.name = std.fs.path.stem(dde_dll_file), |
|
.root_module = b.createModule(.{ |
|
.target = target, |
|
.optimize = optimize, |
|
}), |
|
}); |
|
core_include(dde_dll.root_module, b, tcluuid_include_dir); |
|
dde_dll.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclWinDde.c"), |
|
.flags = stubext_flags.items, |
|
}); |
|
dde_dll.root_module.linkLibrary(finalstublib); |
|
win_syslibs(dde_dll.root_module); |
|
dde_dll.root_module.link_libc = true; |
|
const install_dde_dll = b.addInstallArtifact(dde_dll, .{ |
|
.dest_dir = .{ .override = .{ .custom = "./" ++ script_install_dir_rel ++ "/dde" } }, |
|
}); |
|
|
|
const reg_dll = b.addLibrary(.{ |
|
.linkage = .dynamic, |
|
.name = std.fs.path.stem(reg_dll_file), |
|
.root_module = b.createModule(.{ |
|
.target = target, |
|
.optimize = optimize, |
|
}), |
|
}); |
|
core_include(reg_dll.root_module, b, tcluuid_include_dir); |
|
reg_dll.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclWinReg.c"), |
|
.flags = stubext_flags.items, |
|
}); |
|
reg_dll.root_module.linkLibrary(finalstublib); |
|
win_syslibs(reg_dll.root_module); |
|
reg_dll.root_module.link_libc = true; |
|
const install_reg_dll = b.addInstallArtifact(reg_dll, .{ |
|
.dest_dir = .{ .override = .{ .custom = "./" ++ script_install_dir_rel ++ "/reg" } }, |
|
}); |
|
|
|
// ================================================================ |
|
//thread 2.8 + tclvfs (both hook their installs onto the default install |
|
//step inside the helpers) |
|
const thread_build = try build_tclthread86(tcl_source_folder, "../tclthread", b, target, optimize, finalstublib); |
|
const build_tclthread_step = b.step("build-tclthread", "build the thread 2.8 loadable extension"); |
|
build_tclthread_step.dependOn(&thread_build.lib.step); |
|
|
|
const tclvfs_compile = try tclvfs86.build_tclvfs86(tcl_source_folder, "../tclvfs", b, target, optimize, finalstublib); |
|
const build_tclvfs_step = b.step("build-tclvfs", "build the tclvfs loadable extension"); |
|
build_tclvfs_step.dependOn(&tclvfs_compile.step); |
|
try tclvfs86.install_tclvfs_package("../tclvfs", b, tclvfs_compile); |
|
|
|
//-- Mk4tcl (G-101): the metakit_punk fork's engine + mk::* binding as a |
|
//suite battery - the mk4 container candidate's loadable shape (lib/mk4tcl<v>) |
|
const mk4tcl_compile = try mk4tcl86.build_mk4tcl86(tcl_source_folder, "../metakit", b, target, optimize, finalstublib); |
|
const build_mk4tcl_step = b.step("build-mk4tcl", "build the Mk4tcl loadable extension (metakit_punk fork; includes mk4too)"); |
|
build_mk4tcl_step.dependOn(&mk4tcl_compile.step); |
|
try mk4tcl86.install_mk4tcl_package("../metakit", b, mk4tcl_compile); |
|
|
|
//-- Tk 8.6 (G-100): tk86t.dll + the tk script library + pkgIndex, all hooked |
|
//onto the default install step inside the helper (as the companions above). |
|
const tk_build = try tk86.build_tk86(tcl_source_folder, "../tk86", b, target, optimize, finalstublib); |
|
const build_tk_step = b.step("build-tk", "build the Tk 8.6 loadable extension (tk86t.dll) + tk script library install"); |
|
build_tk_step.dependOn(&tk_build.lib.step); |
|
|
|
// ================================================================ |
|
//install-libraries: the whole script library tree -> lib/tcl8.6 (each |
|
//subdir package keeps its own pkgIndex; encoding/tzdata/msgs ride inside), |
|
//dde/reg dlls into their package dirs, tm modules under lib/tcl8/<v>/ with |
|
//versions parsed from the library pkgIndex files (makefile.vc nmakehlp -V |
|
//parity). NOTE the whole-tree copy keeps http/msgcat/tcltest/platform as |
|
//directory packages TOO (stock nmake installs those four as .tm only) - |
|
//harmless duplication (same versions; the tm loader wins), simpler install. |
|
const install_libraries = b.step("install-libraries", "install the tcl script library tree, dde/reg package dlls and tm modules"); |
|
const install_library_tree = b.addInstallDirectory(.{ |
|
.source_dir = b.path(tcl_source_folder ++ "/library"), |
|
.install_dir = .prefix, |
|
.install_subdir = script_install_dir_rel, |
|
}); |
|
install_libraries.dependOn(&install_library_tree.step); |
|
install_libraries.dependOn(&install_dde_dll.step); |
|
install_libraries.dependOn(&install_reg_dll.step); |
|
|
|
const ver_http = common.pkgIfneededVersion(b, tcl_source_folder ++ "/library/http/pkgIndex.tcl", "http"); |
|
const ver_msgcat = common.pkgIfneededVersion(b, tcl_source_folder ++ "/library/msgcat/pkgIndex.tcl", "msgcat"); |
|
const ver_tcltest = common.pkgIfneededVersion(b, tcl_source_folder ++ "/library/tcltest/pkgIndex.tcl", "tcltest"); |
|
const ver_platform = common.pkgIfneededVersion(b, tcl_source_folder ++ "/library/platform/pkgIndex.tcl", "platform"); |
|
const ver_pshell = common.pkgIfneededVersion(b, tcl_source_folder ++ "/library/platform/pkgIndex.tcl", "platform::shell"); |
|
std.debug.print("tm versions: http {s}, msgcat {s}, tcltest {s}, platform {s}, platform::shell {s}\n", .{ ver_http, ver_msgcat, ver_tcltest, ver_platform, ver_pshell }); |
|
const tm_installs = [_]struct { src: []const u8, subdir: []const u8, tmbase: []const u8, ver: []const u8 }{ |
|
.{ .src = "/library/http/http.tcl", .subdir = "8.6", .tmbase = "http", .ver = ver_http }, |
|
.{ .src = "/library/msgcat/msgcat.tcl", .subdir = "8.5", .tmbase = "msgcat", .ver = ver_msgcat }, |
|
.{ .src = "/library/tcltest/tcltest.tcl", .subdir = "8.5", .tmbase = "tcltest", .ver = ver_tcltest }, |
|
.{ .src = "/library/platform/platform.tcl", .subdir = "8.6", .tmbase = "platform", .ver = ver_platform }, |
|
.{ .src = "/library/platform/shell.tcl", .subdir = "8.6", .tmbase = "platform/shell", .ver = ver_pshell }, |
|
}; |
|
for (tm_installs) |tmi| { |
|
const tm_inst = b.addInstallFileWithDir(b.path(b.fmt("{s}{s}", .{ tcl_source_folder, tmi.src })), .prefix, b.pathJoin(&.{ module_install_dir_rel, tmi.subdir, b.fmt("{s}-{s}.tm", .{ tmi.tmbase, tmi.ver }) })); |
|
install_libraries.dependOn(&tm_inst.step); |
|
} |
|
|
|
// ================== post-build phases (built-shell runs) ================== |
|
//Native-host only: these execute the built artifacts. All runs use the |
|
//INSTALLED shells beside the installed lib tree with a scrubbed environment |
|
//and NO TCL_LIBRARY - every run doubles as the hermetic-resolution proof the |
|
//G-099 acceptance requires (exe/dll-relative ../lib/tcl8.6 discovery). |
|
if (target.result.os.tag == builtin.os.tag) { |
|
const installed_static = b.pathJoin(&.{ b.install_path, "bin", tclsh_static ++ ".exe" }); |
|
const installed_dyn = b.pathJoin(&.{ b.install_path, "bin", tclsh_dynamic ++ ".exe" }); |
|
const testreports_dir = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "testreports" }), "\\", "/"); |
|
|
|
//-- smoke: both shells report the expected patchlevel and resolve the |
|
//installed library (tzdata + autoload prove init.tcl + the tree) |
|
const smoke_step = b.step("smoke", "smoke-test the installed shells (patchlevel + installed-library resolution + package loads)"); |
|
const shell_runs = [_]struct { exe: []const u8, label: []const u8 }{ |
|
.{ .exe = installed_static, .label = "static" }, |
|
.{ .exe = installed_dyn, .label = "dynamic" }, |
|
}; |
|
for (shell_runs) |sr| { |
|
const smoke = b.addSystemCommand(&.{sr.exe}); |
|
common.scrubTclEnv(smoke); |
|
smoke.has_side_effects = true; |
|
smoke.setName(b.fmt("smoke {s}", .{sr.label})); |
|
smoke.addFileArg(b.path("tools/suite_smoke.tcl")); |
|
smoke.addArgs(&.{ "-mode", "installed", "-expect", tcl_h_patchlevel }); |
|
smoke.step.dependOn(b.getInstallStep()); |
|
smoke.step.dependOn(install_libraries); |
|
smoke_step.dependOn(&smoke.step); |
|
//package-resolution smoke: the built companions + the library-dir |
|
//packages load in each shell (Thread/vfs via lib/, registry/dde via |
|
//the script library's package dirs - static registrations in the |
|
//static shell, the built dlls in the dynamic one; Tk via lib/tk8.6 |
|
//under a withdrawn root, so no window flashes here) |
|
const pkgs = b.addSystemCommand(&.{sr.exe}); |
|
common.scrubTclEnv(pkgs); |
|
pkgs.has_side_effects = true; |
|
pkgs.setName(b.fmt("pkg_smoke {s}", .{sr.label})); |
|
pkgs.addFileArg(b.path("tools/pkg_smoke.tcl")); |
|
pkgs.addArgs(&.{ "-gui", "1", "Thread", "vfs", "registry", "dde", "Tk", "Mk4tcl" }); |
|
pkgs.step.dependOn(b.getInstallStep()); |
|
pkgs.step.dependOn(install_libraries); |
|
smoke_step.dependOn(&pkgs.step); |
|
} |
|
|
|
//-- Tk window-station smoke (G-100 acceptance evidence): maps a real |
|
//toplevel with classic+themed widgets, updates, then destroys - a |
|
//load-only check would pass without a usable display. Static shell only: |
|
//the pkg_smoke runs above already prove Tk LOADS in both. |
|
const tksmoke = b.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(tksmoke); |
|
tksmoke.has_side_effects = true; |
|
tksmoke.setName("tk_smoke static"); |
|
tksmoke.addFileArg(b.path("tools/tk_smoke.tcl")); |
|
tksmoke.addArgs(&.{ "-expect", tk_build.patchlevel }); |
|
tksmoke.step.dependOn(b.getInstallStep()); |
|
tksmoke.step.dependOn(install_libraries); |
|
smoke_step.dependOn(&tksmoke.step); |
|
|
|
// ============ G-100 script-library dependency set ============ |
|
//Two shell roles here, deliberately split: |
|
// INSTALLER/CRITCL runs use the build-cache artifact (addRunArtifact) so |
|
// the step is CACHED on the shell's own hash and can own output dirs. |
|
// That exe has no ../lib/tcl8.6 beside it, so those runs (and only |
|
// those) get TCL_LIBRARY pointed at the staged source library. |
|
// SMOKE runs use the INSTALLED shell with no TCL_LIBRARY, exactly as the |
|
// smokes above: package resolution then comes from <prefix>/lib via the |
|
// exe-relative auto_path entry - which is also what proves the install |
|
// layout, not just the build. |
|
const tcl_library_src = common.replaceAll(b, b.pathFromRoot(tcl_source_folder ++ "/library"), "\\", "/"); |
|
|
|
//-- tklib: pure-tcl, but its installer is NOT a straight copy (module |
|
//exclusion list, app-script generation, and generation of the aggregating |
|
//top-level pkgIndex.tcl that makes the two-levels-down module indexes |
|
//resolvable). Batch mode, packages only; -pkg-path is a cached output dir |
|
//installed into the prefix, so the installer's own default (derived from |
|
//the RUNNING shell's tcl_pkgPath) can never place files off-tree. |
|
const tklib_ver = common.installerPackageVersion(b, "../tklib"); |
|
const tklib_step = b.step("tklib", "install tklib via its own installer under the suite-built 8.6 shell (+Tk-dependent package smoke)"); |
|
const tklib_run = b.addRunArtifact(tclsh_static_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"'). 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.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(tklib_smoke); |
|
tklib_smoke.has_side_effects = true; |
|
tklib_smoke.setName("tklib pkg_smoke"); |
|
tklib_smoke.addFileArg(b.path("tools/pkg_smoke.tcl")); |
|
tklib_smoke.addArgs(&.{ "-gui", "1", "tooltip" }); //a Tk-DEPENDENT tklib package |
|
tklib_smoke.step.dependOn(&tklib_install.step); |
|
tklib_smoke.step.dependOn(b.getInstallStep()); //Tk dll + pkgIndex + script library |
|
tklib_smoke.step.dependOn(install_libraries); |
|
tklib_step.dependOn(&tklib_smoke.step); |
|
|
|
//-- tcllib: same installer family (its critcl DSL procs are stubbed to |
|
//no-ops there - this is the PURE-TCL side; the accelerators are the |
|
//tcllibc step below). |
|
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 8.6 shell (+smoke)"); |
|
const tcllib_run = b.addRunArtifact(tclsh_static_exe); |
|
common.scrubTclEnv(tcllib_run); |
|
tcllib_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src); |
|
_ = 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"); |
|
//tcllib ships pkgIndex.tcl 'ifneeded' versions that disagree with what the |
|
//indexed files 'provide' - 'package require rest' is dead on arrival in |
|
//every kit built from it (upstream tkt 523f7e0556, unfixed on trunk). The |
|
//gate repairs the RECORDED defects between the installer output and the |
|
//install tree, and FAILS the build on any unrecorded one rather than |
|
//silently masking a genuine upstream bump - see tools/pkgindex_gate.tcl. |
|
const tcllib_gate = b.addRunArtifact(tclsh_static_exe); |
|
common.scrubTclEnv(tcllib_gate); |
|
tcllib_gate.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src); |
|
_ = tcllib_gate.captureStdOut(.{}); |
|
tcllib_gate.addFileArg(b.path("tools/pkgindex_gate.tcl")); |
|
tcllib_gate.addDirectoryArg(tcllib_out); |
|
const tcllib_gated = tcllib_gate.addOutputDirectoryArg("tcllib-pkg-gated"); |
|
const tcllib_install = b.addInstallDirectory(.{ |
|
.source_dir = tcllib_gated, |
|
.install_dir = .prefix, |
|
.install_subdir = b.fmt("lib/tcllib{s}", .{tcllib_ver}), |
|
}); |
|
const tcllib_smoke = b.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(tcllib_smoke); |
|
tcllib_smoke.has_side_effects = true; |
|
tcllib_smoke.setName("tcllib pkg_smoke"); |
|
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_smoke.step.dependOn(install_libraries); |
|
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). critcl compiles against ITS OWN bundled headers, chosen by |
|
//the version of the shell running it (critcl_c/tcl8.6 here) - which is |
|
//why the generation split works with one config file. sak.tcl's critcl |
|
//target expects a tclkitsh on windows, so this replicates its underlying |
|
//invocation directly, as suite_tcl90 does. |
|
const tcllibc_step = b.step("tcllibc", "build+install tcllibc (critcl accelerators, zig cc) under the suite-built 8.6 shell (+accel smoke/bench)"); |
|
const critcl_run = b.addRunArtifact(tclsh_static_exe); |
|
common.scrubTclEnv(critcl_run); |
|
critcl_run.setEnvironmentVariable("TCL_LIBRARY", tcl_library_src); |
|
_ = 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, and a scrubbed |
|
//environment falls back to critcl's builtin msvc 'cl' target (G-102 |
|
//finding, inherited here). |
|
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.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(tcllibc_smoke); |
|
tcllibc_smoke.has_side_effects = true; |
|
tcllibc_smoke.setName("tcllibc accel smoke"); |
|
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_smoke.step.dependOn(install_libraries); |
|
//accelerated-vs-pure evidence (G-100 acceptance): same payload through |
|
//sha1's critcl and pure-Tcl implementations, digests compared, ratio |
|
//printed - the punkcheck-relevant hashing comparison. |
|
const accel_bench = b.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(accel_bench); |
|
accel_bench.has_side_effects = true; |
|
accel_bench.setName("tcllibc accel bench"); |
|
accel_bench.addFileArg(b.path("tools/accel_bench.tcl")); |
|
accel_bench.step.dependOn(&tcllibc_smoke.step); |
|
tcllibc_step.dependOn(&accel_bench.step); |
|
|
|
//-- library-artifacts: punkbin LIB-TIER emission (G-138) - the tcl8 |
|
//GENERATION of the library packages this suite builds and verifies |
|
//(tcllib pure-tcl -> lib/allplatforms, tcllibc critcl accelerators -> |
|
//lib/win32-x86_64) as immutable -tcl8-r<N> zips with embedded schema-v2 |
|
//class=library records, toml sidecars and per-tier sha1sums. Forked from |
|
//suite_tcl90/build905.zig's step (the recorded duplication posture); key |
|
//differences: runs under the INSTALLED STATIC SHELL (this suite has no |
|
//kit family - the smoke-run pattern above; sha1/tcllibc resolve from |
|
//<prefix>/lib, so emission still proves the built shell executes the |
|
//very packages being published), and the generation tag is tcl8 (folder |
|
//names collide across generations: both install tcllib2.0 + tcllibc). |
|
//punkzip: staged upstream checkout (sources.config / build.zig.zon pin), |
|
//compiled for the build host; ReleaseSafe deliberately (real deflate |
|
//work). Revisions bump ONLY on deliberate publish (-Dlibrev; the dlls |
|
//are not bit-reproducible across zig rebuilds). Publication to the real |
|
//punkbin repo stays a deliberate user step. |
|
const library_artifacts_step = b.step("library-artifacts", "emit punkbin lib-tier artifacts (<pkg>-tcl8-r<N>.zip + toml + sha1sums) from the verified tcllib/tcllibc installs -> <prefix>/library/punkbin/lib"); |
|
const librev = b.option(u32, "librev", "assembly revision N for the -r<N> library artifact names (library-artifacts; default 2 - r2 published 2026-08-08 with the pkgindex_gate repairs manifest, r1 2026-07-30). The default IS the committed record of published state - bump it in the same work unit as each publish.") orelse 2; |
|
const punkzip_exe = b.addExecutable(.{ |
|
.name = "punkzip", |
|
.root_module = b.createModule(.{ |
|
.root_source_file = b.path("../punkzip/src/punkzip.zig"), |
|
.target = b.graph.host, |
|
.optimize = .ReleaseSafe, |
|
}), |
|
}); |
|
//G-117 schema identity options (introduced here by the library step - |
|
//this suite emits no runtime-family artifacts): origin = canonical |
|
//artifact repo the artifacts are BUILT FOR; project/projecturl/packager |
|
//default from the enclosing project's punkproject.toml [project] table |
|
//(common.punkprojectField: bounded walk-up from the build root - covers |
|
//the suite source dir and the staged _build recipe dir alike; a copied |
|
//suite reads ITS project's manifest). packager = declared identity, |
|
//resolution chain -Dpackager > env PUNKBIN_PACKAGER > punkproject.toml |
|
//[project] packager > building checkout's git identity > 'fossil user |
|
//default' > "unrecorded". |
|
const originurl = b.option([]const u8, "originurl", "canonical artifact repo url the library artifacts are built FOR ('origin' field; default punkbin)") orelse "https://www.gitea1.intx.com.au/jn/punkbin"; |
|
const project = b.option([]const u8, "project", "project name recorded in the artifact records ('project' field; default: punkproject.toml [project] name, else punkshell)") orelse common.punkprojectField(b, "name") orelse "punkshell"; |
|
const projecturl = b.option([]const u8, "projecturl", "project url recorded in the artifact records ('project_url' field; default: punkproject.toml [project] url, else unrecorded)") orelse common.punkprojectField(b, "url") orelse "unrecorded"; |
|
const packager = blk: { |
|
if (b.option([]const u8, "packager", "declared packager identity for the artifact records (-Dpackager > env PUNKBIN_PACKAGER > punkproject.toml [project] packager > git identity > fossil user default > unrecorded)")) |p| break :blk p; |
|
if (b.graph.environ_map.get("PUNKBIN_PACKAGER")) |p| { |
|
if (p.len != 0) break :blk p; |
|
} |
|
if (common.punkprojectField(b, "packager")) |p| break :blk p; |
|
//out_code is only meaningful on some runAllowFail paths - judge |
|
//success by usable trimmed output, not the code |
|
var gc: u8 = undefined; |
|
const gname: ?[]const u8 = b.runAllowFail(&.{ "git", "config", "user.name" }, &gc, .ignore) catch null; |
|
if (gname != null) { |
|
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) { |
|
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; |
|
} |
|
} |
|
//fossil leg: the fossil-only project fallback - bare default user |
|
//(fossil has no email convention); declarative like the rest. |
|
const fuser: ?[]const u8 = b.runAllowFail(&.{ "fossil", "user", "default" }, &gc, .ignore) catch null; |
|
if (fuser != null) { |
|
const fu = std.mem.trim(u8, fuser.?, " \t\r\n"); |
|
if (fu.len != 0) break :blk b.dupe(fu); |
|
} |
|
break :blk "unrecorded"; |
|
}; |
|
//source checkout provenance: tcllib manifest.uuid (suite.tcl's fossil |
|
//staging materializes it); critcl commit via best-effort git probe - |
|
//bootstrap-flow tarballs carry no .git, 'unrecorded' then. |
|
const uuid_tcllib = common.manifestUuid(b, "../tcllib"); |
|
const critcl_uuid = blk: { |
|
var gc: u8 = undefined; |
|
const out: ?[]const u8 = b.runAllowFail(&.{ "git", "-C", b.pathFromRoot("../critcl"), "rev-parse", "HEAD" }, &gc, .ignore) catch null; |
|
if (out != null) { |
|
const u = std.mem.trim(u8, out.?, " \t\r\n"); |
|
if (u.len == 40) break :blk b.dupe(u); |
|
} |
|
break :blk "unrecorded"; |
|
}; |
|
const libart_outdir = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "library", "punkbin" }), "\\", "/"); |
|
const tcllib_installed = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "lib", b.fmt("tcllib{s}", .{tcllib_ver}) }), "\\", "/"); |
|
const tcllibc_installed = common.replaceAll(b, b.pathJoin(&.{ b.install_path, "lib", "tcllibc" }), "\\", "/"); |
|
const lib_emit = b.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(lib_emit); |
|
lib_emit.has_side_effects = true; |
|
lib_emit.setName("library_artifacts emission"); |
|
lib_emit.addFileArg(b.path("tools/library_artifacts.tcl")); |
|
lib_emit.addArgs(&.{ "-outdir", libart_outdir, "-rev", b.fmt("{d}", .{librev}), "-generation", "tcl8", "-target", "win32-x86_64" }); |
|
lib_emit.addArgs(&.{ "-tclpatch", tcl_h_patchlevel, "-suite", "suite_tcl86", "-zig", builtin.zig_version_string, "-optimize", @tagName(optimize) }); |
|
lib_emit.addArgs(&.{ "-origin", originurl, "-packager", packager, "-project", project, "-projecturl", projecturl }); |
|
lib_emit.addArgs(&.{ "-provenance", b.fmt("tcllib {s} critcl {s}", .{ uuid_tcllib, critcl_uuid }) }); |
|
lib_emit.addArg("-punkzip"); |
|
lib_emit.addArtifactArg(punkzip_exe); |
|
lib_emit.addArgs(&.{ "-testreports", testreports_dir }); |
|
//5-tuples: folder tier license testlibs {path} - tcllibc's [tests] |
|
//evidence is the tcllib suite run (it IS tcllib's accelerators; the |
|
//accel smoke above proved the bundled md5 accelerator loads) |
|
lib_emit.addArgs(&.{ "-packages", b.fmt("tcllib{s} allplatforms BSD tcllib {{{s}}} tcllibc win32-x86_64 BSD tcllib {{{s}}}", .{ tcllib_ver, tcllib_installed, tcllibc_installed }) }); |
|
//verified inputs only: the pure-tcl + accelerator smokes (accel_bench |
|
//rides tcllibc_step, not this dependency chain) |
|
lib_emit.step.dependOn(&tcllib_smoke.step); |
|
lib_emit.step.dependOn(&tcllibc_smoke.step); |
|
library_artifacts_step.dependOn(&lib_emit.step); |
|
|
|
//-- test-gate: the 8.6 core testsuite under the installed static shell, |
|
//judged on PARSED totals (all.tcl exit codes lie) vs the tracked |
|
//dispositioned baseline. First census: run with -Dtestmode=record (the |
|
//baseline file deliberately does not exist until the census is |
|
//dispositioned - G-099). |
|
const gate_step = b.step("test-gate", "tcl 8.6 core testsuite under the installed static 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 testmode_opt = b.option([]const u8, "testmode", "core test-gate mode: gate|record (record = census run, no baseline needed)") orelse "gate"; |
|
const gate_run = b.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(gate_run); |
|
gate_run.has_side_effects = true; |
|
gate_run.setName("core test-gate"); |
|
gate_run.addFileArg(b.path("tools/test_gate.tcl")); |
|
gate_run.addArgs(&.{ "-library", "tclcore", "-mode", testmode_opt }); |
|
gate_run.addArg("-testsdir"); |
|
gate_run.addArg(common.replaceAll(b, b.pathFromRoot(tcl_source_folder ++ "/tests"), "\\", "/")); |
|
if (std.mem.eql(u8, testmode_opt, "gate")) { |
|
gate_run.addArg("-baseline"); |
|
if (common.pathExists(b, "expected_test_failures.txt")) { |
|
gate_run.addFileArg(b.path("expected_test_failures.txt")); |
|
} else { |
|
//missing baseline: pass the conventional path so the engine's |
|
//message names the file to create after the record-mode census |
|
gate_run.addArg(common.replaceAll(b, b.pathFromRoot("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_run.step.dependOn(install_libraries); |
|
gate_step.dependOn(&gate_run.step); |
|
|
|
//-- per-library testsuites (suite_tcl90 G-107 pattern): policy tiers |
|
//gate|record|skip resolved at invocation. |
|
// -Dtestpolicy-<lib>=gate|record|skip -Dtestargs-<lib>="..." |
|
// -Dtestnotfiles-<lib>="a.test b.test" |
|
//Baselines expected_test_failures_thread.txt / _tclvfs.txt are created |
|
//from dispositioned record-mode censuses (same first-census flow as the |
|
//core gate); the G-100 script-library suites ride the RECORD tier (their |
|
//censuses are evidence, not a gate criterion - the same disposition |
|
//suite_tcl90 made). |
|
//NOTE zig runs independent steps concurrently - drive combined test |
|
//invocations with -j1 (suite.tcl's test action does) so timing-sensitive |
|
//suites never run beside heavy ones. |
|
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{ |
|
.{ .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). |
|
//The installed tcllibc is on the shell's package path, so |
|
//accelerator-aware suites engage the critcl accelerators - the |
|
//highest-value run of the set for an 8.6 runtime nobody else builds. |
|
.{ .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: OPT-IN (not part of test-libraries) - it maps windows on the |
|
//interactive desktop for tens of minutes. The native-dialog suites are |
|
//excluded by default: their message-injection automation does not |
|
//drive the real OS dialogs under a batch shell, so each case blocks |
|
//waiting on a human. Re-include with -Dtestnotfiles-tk="" for a |
|
//manual interactive run. |
|
.{ .name = "tk", .testsdir = "../tk86/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 installed static 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 installed static 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.addSystemCommand(&.{installed_static}); |
|
common.scrubTclEnv(run); |
|
run.has_side_effects = true; |
|
run.setName(b.fmt("test {s}", .{ls.name})); |
|
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 and common.pathExists(b, ls.baseline)) { |
|
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 |
|
const conv = if (ls.baseline.len != 0) ls.baseline else b.fmt("expected_test_failures_{s}.txt", .{ls.name}); |
|
run.addArg(common.replaceAll(b, b.pathFromRoot(conv), "\\", "/")); |
|
} |
|
} |
|
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()); |
|
run.step.dependOn(install_libraries); |
|
step.dependOn(&run.step); |
|
} |
|
|
|
// ================================================================ |
|
//mk4 kit-head (G-101 demonstration arm): a SECOND static shell that |
|
//boots from its own attached Metakit container - engine + Mk4tcl/ |
|
//mk4too + the tclvfs C core compiled in, tclAppInit overlaid with the |
|
//build_kithead86/kithead.c hooks, boot.tcl riding IN the attached vfs |
|
//(tclkit convention; the setupvfs role is played by |
|
//tools/mk4kit_attach.tcl under the installed dynamic shell). |
|
//Artifact name per the recorded trial convention (G-103 family name + |
|
//container tag riding the modifier chain while container trials run): |
|
//bin/tclsh<patchlevel>-mk4-r1.exe. The raw un-attached head stays a |
|
//cache artifact - only the attached runtime installs. Opt-in step |
|
//('kithead'), not part of the default build. |
|
const kithead_raw = b.addExecutable(.{ |
|
.name = "kithead86raw", |
|
.root_module = b.createModule(.{ |
|
.root_source_file = b.path("src/main.zig"), |
|
.target = target, |
|
.optimize = optimize, |
|
}), |
|
}); |
|
kithead_raw.stack_size = 2300000; //makefile.vc CONEXECMD parity (as tclsh86ts) |
|
kithead_raw.subsystem = .Console; |
|
core_include(kithead_raw.root_module, b, tcluuid_include_dir); |
|
for (core_sources.items) |src| { |
|
const flags = if (std.mem.eql(u8, std.fs.path.stem(src), "tclMain")) mainw_static_flags.items else pkg_static_flags.items; |
|
kithead_raw.root_module.addCSourceFile(.{ .file = b.path(src), .flags = flags }); |
|
} |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/generic/tclMain.c"), |
|
.flags = pkg_static_flags.items, |
|
}); |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclWinReg.c"), |
|
.flags = stubext_static_flags.items, |
|
}); |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/win/tclWinDde.c"), |
|
.flags = stubext_static_flags.items, |
|
}); |
|
//tclAppInit overlay - two injections around the stock file: the |
|
//kithead pre-hook (static registrations + TclSetPreInitScript) BEFORE |
|
//Tcl_Init, the post-hook (attached-main.tcl startup pivot) before the |
|
//rcFileName epilogue. Anchors are exact stock-8.6.18 lines; a missed |
|
//anchor leaves the file unchanged and the kithead smoke below fails |
|
//on the missing ::tcl::kitpath - that smoke is the overlay's guard. |
|
const kithead_pre_anchor = " if ((Tcl_Init)(interp) == TCL_ERROR) {"; |
|
const kithead_pre_inject = |
|
\\ /* mk4 kit-head boot hooks (punkshell G-101; build_kithead86/kithead.c) */ |
|
\\ { |
|
\\ extern int TclKitHead_PreTclInit(Tcl_Interp *); |
|
\\ if (TclKitHead_PreTclInit(interp) != TCL_OK) { |
|
\\ return TCL_ERROR; |
|
\\ } |
|
\\ } |
|
\\ |
|
; |
|
const kithead_post_anchor = " (Tcl_ObjSetVar2)(interp, Tcl_NewStringObj(\"tcl_rcFileName\", -1), NULL,"; |
|
const kithead_post_inject = |
|
\\ /* mk4 kit-head startup pivot (punkshell G-101) */ |
|
\\ { |
|
\\ extern int TclKitHead_PostTclInit(Tcl_Interp *); |
|
\\ TclKitHead_PostTclInit(interp); |
|
\\ } |
|
\\ |
|
; |
|
var kithead_appinit_src = common.readSourceFile(b, tcl_source_folder ++ "/win/tclAppInit.c"); |
|
kithead_appinit_src = common.replaceAll(b, kithead_appinit_src, kithead_pre_anchor, kithead_pre_inject ++ kithead_pre_anchor); |
|
kithead_appinit_src = common.replaceAll(b, kithead_appinit_src, kithead_post_anchor, kithead_post_inject ++ kithead_post_anchor); |
|
const wf_kithead_appinit = b.addWriteFiles(); |
|
const kithead_appinit_overlay = wf_kithead_appinit.add("tclAppInit.c", kithead_appinit_src); |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = kithead_appinit_overlay, |
|
.flags = appinit_static_flags.items, |
|
}); |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = b.path("build_kithead86/kithead.c"), |
|
.flags = appinit_static_flags.items, |
|
}); |
|
inline for (.{ "tclStubLib", "tclTomMathStubLib", "tclOOStubLib" }) |nm| { |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = b.path(tcl_source_folder ++ "/generic/" ++ nm ++ ".c"), |
|
.flags = stub_flags.items, |
|
}); |
|
} |
|
//metakit engine + binding, compiled in - the same source lists the |
|
//build_mk4tcl86 dll module carries (pub there so the two stay in |
|
//step); the binding uses the static flag pair the G-153 experiment |
|
//proved (USE_TCL_STUBS + STATIC_BUILD) |
|
kithead_raw.root_module.addIncludePath(b.path("../metakit/src/include")); |
|
kithead_raw.root_module.addIncludePath(b.path("../metakit/src/core")); |
|
inline for (mk4tcl86.core_sources) |nm| { |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = b.path("../metakit/src/core/" ++ nm), |
|
.flags = &mk4tcl86.engine_flags, |
|
}); |
|
} |
|
inline for (.{ "mk4tcl.cpp", "mk4too.cpp" }) |nm| { |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = b.path("../metakit/src/tcl/" ++ nm), |
|
.flags = &mk4tcl86.binding_static_flags, |
|
}); |
|
} |
|
//tclvfs C core, static: build_tclvfs86's define set with STATIC_BUILD |
|
//in place of BUILD_vfs, same include-order shim (that module's |
|
//__stat64 note) |
|
const kithead_vfsinfo = tclvfs86.versionInfo(b, "../tclvfs"); |
|
const wf_kithead_vfs = b.addWriteFiles(); |
|
const kithead_vfs_shim = wf_kithead_vfs.add("vfs_statorder_shim.c", |
|
\\/* generated include-order shim - see build_tclvfs86.zig */ |
|
\\#include <sys/stat.h> |
|
\\#include "vfs.c" |
|
\\ |
|
); |
|
kithead_raw.root_module.addCSourceFile(.{ |
|
.file = kithead_vfs_shim, |
|
.flags = &.{ |
|
"-DPACKAGE_NAME=\"vfs\"", |
|
"-DPACKAGE_TARNAME=\"vfs\"", |
|
b.fmt("-DPACKAGE_VERSION=\"{s}\"", .{kithead_vfsinfo.dotversion}), |
|
b.fmt("-DPACKAGE_STRING=\"vfs {s}\"", .{kithead_vfsinfo.dotversion}), |
|
"-DPACKAGE_BUGREPORT=\"\"", |
|
"-DPACKAGE_URL=\"\"", |
|
"-DHAVE_SYS_STAT_H=1", |
|
"-DTCL_CFG_OPTIMIZED=1", |
|
"-DUSE_TCL_STUBS=1", |
|
"-DSTATIC_BUILD", |
|
}, |
|
}); |
|
kithead_raw.root_module.addIncludePath(b.path("../tclvfs")); |
|
kithead_raw.root_module.addIncludePath(b.path("../tclvfs/generic")); |
|
kithead_raw.root_module.linkLibrary(zlib_lib_compile); |
|
win_syslibs(kithead_raw.root_module); |
|
kithead_raw.root_module.link_libc = true; |
|
kithead_raw.root_module.link_libcpp = true; |
|
kithead_raw.root_module.addWin32ResourceFile(.{ |
|
.file = tclshrc_overlay, |
|
.include_paths = &.{ |
|
b.path(tcl_source_folder ++ "/generic"), |
|
b.path(tcl_source_folder ++ "/win"), |
|
}, |
|
}); |
|
|
|
//attach the runtime vfs (boot.tcl + lib/tcl8.6 + lib/tcl8 + tclvfs |
|
//scripts w/ static-adjusted pkgIndex) -> the installed head artifact |
|
const kithead_name = b.fmt("tclsh{s}-mk4-r1.exe", .{tcl_h_patchlevel}); |
|
const kithead_out = common.replaceAll(b, b.pathJoin(&.{ bindir, kithead_name }), "\\", "/"); |
|
const kithead_attach = b.addSystemCommand(&.{installed_dyn}); |
|
common.scrubTclEnv(kithead_attach); |
|
kithead_attach.has_side_effects = true; |
|
kithead_attach.setName("mk4kit attach"); |
|
kithead_attach.addFileArg(b.path("tools/mk4kit_attach.tcl")); |
|
kithead_attach.addArg("-exe"); |
|
kithead_attach.addArtifactArg(kithead_raw); |
|
kithead_attach.addArgs(&.{ "-prefix", prefix, "-out", kithead_out }); |
|
kithead_attach.addArg("-boot"); |
|
kithead_attach.addFileArg(b.path("build_kithead86/boot.tcl")); |
|
kithead_attach.step.dependOn(b.getInstallStep()); |
|
kithead_attach.step.dependOn(install_libraries); |
|
|
|
//the attached head proves in-exe library resolution + package loads |
|
//(runs beside a REAL ../lib/tcl8.6 - the assertions are all in-exe) |
|
const kithead_smoke = b.addSystemCommand(&.{kithead_out}); |
|
common.scrubTclEnv(kithead_smoke); |
|
kithead_smoke.has_side_effects = true; |
|
kithead_smoke.setName("kithead smoke"); |
|
kithead_smoke.addFileArg(b.path("tools/kithead_smoke.tcl")); |
|
kithead_smoke.step.dependOn(&kithead_attach.step); |
|
|
|
const kithead_step = b.step("kithead", "G-101: build the mk4 kit-head, attach its runtime vfs (boot.tcl + lib trees) and smoke the self-mounted boot"); |
|
kithead_step.dependOn(&kithead_smoke.step); |
|
} |
|
} |
|
|
|
//G-102 bootstrap mode (suite_tcl90 parity): registered when this recipe is |
|
//invoked from the TRACKED suite directory. The stage layout matches suite.tcl's |
|
//fossil staging so both flows share the staged invocation; the stage dir |
|
//derives from THIS suite folder's name (copy-and-tweak isolation). |
|
fn bootstrapMode(b: *std.Build) !void { |
|
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", "tcl86" }, |
|
.{ "tclthread", "tclthread" }, |
|
.{ "tclvfs", "tclvfs" }, |
|
//G-100 dependency set |
|
.{ "tk", "tk86" }, |
|
.{ "tklib", "tklib" }, |
|
.{ "tcllib", "tcllib" }, |
|
.{ "critcl", "critcl" }, |
|
//G-138 library-artifacts |
|
.{ "punkzip", "punkzip" }, |
|
//G-101 kit-container work: the metakit_punk fork (engine + Mk4tcl binding) |
|
.{ "metakit", "metakit" }, |
|
}; |
|
var deps_pending = false; |
|
for (source_dirs) |sd| { |
|
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). |
|
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 invalidates 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) |
|
const recipe_items = [_][]const u8{ |
|
"build86.zig", |
|
"build_common.zig", |
|
"build_zlib86", |
|
"build_tclthread86", |
|
"build_tclvfs86", |
|
"build_tk86", |
|
"build_mk4tcl86", |
|
"build_kithead86", |
|
"critcl_zig.config", |
|
"expected_test_failures.txt", |
|
"expected_test_failures_thread.txt", |
|
"expected_test_failures_tclvfs.txt", |
|
"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 (per-zig-version cache dir - |
|
//object caches must never be shared across zig versions; forward-slash paths |
|
//throughout: the prefix flows into -D macros as C string literals) |
|
//default mirrors suite.tcl's build default (keep the two in step) |
|
const steps_opt = b.option([]const []const u8, "steps", "steps for the staged build (repeat the flag; default: install install-libraries smoke tklib tcllib tcllibc library-artifacts)") orelse @as([]const []const u8, &.{ "install", "install-libraries", "smoke", "tklib", "tcllib", "tcllibc", "library-artifacts" }); |
|
var cachever: []const u8 = builtin.zig_version_string; |
|
cachever = common.replaceAll(b, cachever, "+", "_"); |
|
cachever = common.replaceAll(b, cachever, "/", "_"); |
|
cachever = common.replaceAll(b, cachever, ":", "_"); |
|
const nested = b.addSystemCommand(&.{ |
|
b.graph.zig_exe, |
|
"build", |
|
"--build-file", |
|
common.replaceAll(b, b.pathFromRoot(b.pathJoin(&.{ stage_rel, "build", "build86.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) |
|
nested.setCwd(.{ .cwd_relative = b.pathFromRoot(b.pathJoin(&.{ stage_rel, "build" })) }); |
|
//hermetic child shells (parity with suite.tcl - same list as the per-step |
|
//scrub, incl. the TCL<major>_<minor>_TM_PATH module roots) |
|
for (common.tcl_env_vars) |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); |
|
}
|
|
|