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.
 
 
 
 
 
 

141 lines
6.9 KiB

//build_common.zig - shared configure-time helpers for the suite recipe files.
//Forked from suite_tcl90/build_common.zig (G-099 duplication-with-a-note posture)
//plus the 8.6-only pkgIfneededVersion parser (tm module versions derive from the
//library pkgIndex files, mirroring makefile.vc's nmakehlp -V derivation).
//These run while the build graph is being CONSTRUCTED (not as build steps). Reads
//are of staged/fetched source trees; generated content is never written into a
//source tree - it goes through b.addWriteFiles() overlays into the build cache and
//is consumed via overlay include dirs / overlay rc copies.
const std = @import("std");
pub fn pathExists(b: *std.Build, path_from_root: []const u8) bool {
const abs = b.pathFromRoot(path_from_root);
std.Io.Dir.cwd().access(b.graph.io, abs, .{}) catch return false;
return true;
}
pub fn readSourceFile(b: *std.Build, path_from_root: []const u8) []u8 {
const abs = b.pathFromRoot(path_from_root);
return std.Io.Dir.cwd().readFileAlloc(b.graph.io, abs, b.allocator, .limited(8 * 1024 * 1024)) catch |err| {
std.debug.panic("suite recipe: cannot read {s}: {s}", .{ abs, @errorName(err) });
};
}
//value of a '#define <name> "<value>"' line (e.g TCL_PATCH_LEVEL from tcl.h)
pub fn parseDefineString(content: []const u8, name: []const u8) ?[]const u8 {
var it = std.mem.splitScalar(u8, content, '\n');
while (it.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t\r");
if (!std.mem.startsWith(u8, trimmed, "#")) continue;
const after_hash = std.mem.trimStart(u8, trimmed[1..], " \t");
if (!std.mem.startsWith(u8, after_hash, "define")) continue;
const after_def = std.mem.trimStart(u8, after_hash[6..], " \t");
if (!std.mem.startsWith(u8, after_def, name)) continue;
const after_name = after_def[name.len..];
if (after_name.len == 0 or (after_name[0] != ' ' and after_name[0] != '\t')) continue;
const q1 = std.mem.indexOfScalar(u8, after_name, '"') orelse continue;
const rest = after_name[q1 + 1 ..];
const q2 = std.mem.indexOfScalar(u8, rest, '"') orelse continue;
return rest[0..q2];
}
return null;
}
pub fn replaceAll(b: *std.Build, input: []const u8, needle: []const u8, replacement: []const u8) []u8 {
return std.mem.replaceOwned(u8, b.allocator, input, needle, replacement) catch @panic("OOM");
}
//hermetic child shells: steps that run the suite-built tclsh must not see the
//user's machine-level tcl environment (TCLLIBPATH pushing machine package dirs
//misdirects installer default paths and risks false-positive smokes; TCL_LIBRARY/
//TK_LIBRARY misdirect init script resolution). The G-099 acceptance requires
//TCLLIBPATH/TCL_LIBRARY unset in ALL suite child invocations - the 8.6 recipe
//never sets TCL_LIBRARY (built-shell runs execute the INSTALLED shell beside its
//installed lib tree, proving hermetic exe-relative resolution).
pub fn scrubTclEnv(run: *std.Build.Step.Run) void {
for ([_][]const u8{ "TCLLIBPATH", "TCL_LIBRARY", "TK_LIBRARY" }) |ev| {
run.removeEnvironmentVariable(ev);
}
}
//checkout uuid from a tree's manifest.uuid when the repo materializes it (a
//per-repo fossil 'manifest' setting; suite.tcl forces it on for staged
//checkouts), else "unrecorded" so provenance emission stays total.
pub fn manifestUuid(b: *std.Build, tree_from_root: []const u8) []const u8 {
const abs = b.pathFromRoot(b.fmt("{s}/manifest.uuid", .{tree_from_root}));
const data = std.Io.Dir.cwd().readFileAlloc(b.graph.io, abs, b.allocator, .limited(4096)) catch return "unrecorded";
return std.mem.trim(u8, data, " \t\r\n");
}
//package version from a TEA tree's configure.ac 'AC_INIT([name],[version])'
//line (thread, tclvfs, ...). Version-derived artifact names must follow the
//checkout - hardcoded versions stamp mismatched sources (suite_tcl90's G-107
//finding: a thread dll self-reporting the wrong version could never satisfy the
//testsuite's exact-version require).
pub fn acInitVersion(b: *std.Build, tree_from_root: []const u8) []const u8 {
const data = readSourceFile(b, b.fmt("{s}/configure.ac", .{tree_from_root}));
var it = std.mem.splitScalar(u8, data, '\n');
while (it.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t\r");
if (!std.mem.startsWith(u8, trimmed, "AC_INIT")) continue;
//the second [...] group is the version
var rest = trimmed;
var group: usize = 0;
while (std.mem.indexOfScalar(u8, rest, '[')) |open| {
const after = rest[open + 1 ..];
const close = std.mem.indexOfScalar(u8, after, ']') orelse break;
group += 1;
if (group == 2) return b.dupe(after[0..close]);
rest = after[close + 1 ..];
}
}
std.debug.panic("no AC_INIT version in {s}/configure.ac", .{tree_from_root});
}
pub fn stripChars(b: *std.Build, input: []const u8, drop: []const u8) []u8 {
var out = std.array_list.Managed(u8).init(b.allocator);
for (input) |ch| {
if (std.mem.indexOfScalar(u8, drop, ch) != null) continue;
out.append(ch) catch @panic("OOM");
}
return out.items;
}
//8.6 tm module versions: 'package ifneeded <name> <version> ...' from a library
//pkgIndex.tcl (makefile.vc derives PKG_HTTP_VER etc the same way via nmakehlp -V).
//Exact token match on the package name distinguishes 'platform' from
//'platform::shell'.
pub fn pkgIfneededVersion(b: *std.Build, pkgindex_from_root: []const u8, pkgname: []const u8) []const u8 {
const data = readSourceFile(b, pkgindex_from_root);
var lines = std.mem.splitScalar(u8, data, '\n');
while (lines.next()) |line| {
var toks = std.mem.tokenizeAny(u8, line, " \t\r");
const t0 = toks.next() orelse continue;
if (!std.mem.eql(u8, t0, "package")) continue;
const t1 = toks.next() orelse continue;
if (!std.mem.eql(u8, t1, "ifneeded")) continue;
const t2 = toks.next() orelse continue;
if (!std.mem.eql(u8, t2, pkgname)) continue;
const ver = toks.next() orelse continue;
return b.dupe(ver);
}
std.debug.panic("no 'package ifneeded {s}' in {s}", .{ pkgname, pkgindex_from_root });
}
//TCL_WIN_VERSION as the makefiles derive it:
//<dotversion>.<releaselevel>.<patchlevel with a/b/. stripped>, releaselevel
//0=alpha 1=beta 2=final (e.g 8.6 + 8.6.18 -> 8.6.2.8618)
pub fn winResourceVersion(b: *std.Build, dotversion: []const u8, patchlevel: []const u8) []u8 {
const rlevel: u8 = if (std.mem.indexOfScalar(u8, patchlevel, 'a') != null)
'0'
else if (std.mem.indexOfScalar(u8, patchlevel, 'b') != null)
'1'
else
'2';
var stripped = std.array_list.Managed(u8).init(b.allocator);
for (patchlevel) |ch| {
if (ch == 'a' or ch == 'b' or ch == '.') continue;
stripped.append(ch) catch @panic("OOM");
}
return std.fmt.allocPrint(b.allocator, "{s}.{c}.{s}", .{ dotversion, rlevel, stripped.items }) catch @panic("OOM");
}