Browse Source
First real buildsuite: build905.zig (verbatim G-076-validated recipe) + helper modules + tools, driven by suite.tcl which stages the recipe into src/buildsuites/_build/suite_tcl90, arranges sources (tcl fossil core-9-0-branch --nested with FOSSIL_HOME isolation; thread/tclvfs trunk; zlib from the tcl tree's compat/zlib; vqtcl from a local dir), generates the configure-products fresh checkouts lack (tclUuid.h, tclsh.exe.manifest, thread.rc include), builds and smoke-tests. Verified PASS: static + zip shells report 9.0.5 (checkout 1a9c3b9d96), Thread 3.0.1 builds from current trunk (new generated-include fix in build_tclthread) and loads. The 2024 TCLSH_PIPEREPL/tclZipfs/tclCmdIL patches are now tracked under suite_tcl90/patches with a semantics README (not applied by default builds). Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
19 changed files with 4738 additions and 1 deletions
@ -0,0 +1,77 @@
|
||||
# suite_tcl90 - zig build of the Tcl 9.0 windows runtime (G-096) |
||||
|
||||
The first real, tracked buildsuite: reproducibly builds `tclsh90s.exe` (static) and |
||||
`tclsh90szip.exe` (self-contained zipfs) plus the thread/vfs/vlerq extension dlls from |
||||
a fresh Tcl core-9-0-branch checkout, using zig only (no MS toolchain, per the project |
||||
toolchain policy). |
||||
|
||||
Everything transient lives under `src/buildsuites/_build/suite_tcl90/` (VCS-ignored via |
||||
the repo's existing `_build` globs): staged recipe, source checkouts, zig cache, and the |
||||
`out/` install prefix. The tracked content here is the recipe and its driver. |
||||
|
||||
## Usage |
||||
|
||||
tclsh suite.tcl build ?options? |
||||
tclsh suite.tcl clean |
||||
|
||||
See `suite.tcl`'s header for options (`-zig`, `-optimize`, `-tclbranch`, `-refresh`, |
||||
`-vqtcl-source`, `-steps`). The zig executable defaults to `$env(PUNK_ZIG)` or `zig` on |
||||
PATH; the recipe's known-good zig is 0.14.0-dev.2074 (see Lineage) - a punk-getzig |
||||
fetched pinned zig is the intended source (G-096 acceptance; wrapper rename pending). |
||||
|
||||
## Layout |
||||
|
||||
- `build905.zig` - the zig recipe (verbatim lineage copy - see Lineage). Expects its |
||||
build root's SIBLING dirs to hold sources (`../tcl905`, `../zlib`, `../tclthread`, |
||||
`../tclvfs`, `../vqtcl`), which is why `suite.tcl` stages it into |
||||
`_build/suite_tcl90/build/` rather than building in place. |
||||
- `build_libtommath/ build_zlib/ build_tclvfs/ build_vqtcl/ build_tclthread/` - helper |
||||
build modules. The four tcl-dependent helpers take the tcl source dir as their |
||||
`tcldir` param (a hardcoded `../tcl90/generic` include, marked "todo - fix" upstream, |
||||
was fixed when these copies were made). `build_libtommath.zig` still carries the |
||||
hardcoded path but is not called by the current recipe (tcl's in-tree tommath is |
||||
compiled into the core; the `libtommath.dll` seen in old output dirs was a stale |
||||
cache-install artifact). |
||||
- `src/main.zig` - stub root module that disables zig's own start symbols so the mingw |
||||
CRT provides the exe entry. |
||||
- `tools/` - `wrapfiletofile.zig` (uuid-header generation used by prepare-source), |
||||
`zipfs_mkzip.tcl` / `zipfs_mkimg.tcl` (make-zipfs drivers). |
||||
- `patches/` - recovered 2024 experiment patches, including TCLSH_PIPEREPL |
||||
(see `patches/README.md`). NOT applied by the default build - stock sources. |
||||
- `suite.tcl` - the driver: stage recipe -> arrange sources -> generate the |
||||
configure-produced files a fresh checkout lacks -> build -> smoke-test. |
||||
|
||||
## Sources |
||||
|
||||
| dir (sibling of build root) | origin | selection | |
||||
|---|---|---| |
||||
| `tcl905` | fossil `https://core.tcl-lang.org/tcl` (clone cached `~/.fossils/tcl.fossil`) | `-tclbranch`, default `core-9-0-branch` | |
||||
| `zlib` | the tcl checkout's own `compat/zlib` (version-matched, includes contrib/minizip) | copied by suite.tcl | |
||||
| `tclthread` | fossil `https://core.tcl-lang.org/thread` | trunk | |
||||
| `tclvfs` | fossil `https://core.tcl-lang.org/tclvfs` | trunk | |
||||
| `vqtcl` | local dir only (vlerq 4.1 by jcw; no public upstream confirmed) | `-vqtcl-source` | |
||||
|
||||
Checkouts under `_build` use `FOSSIL_HOME=<stage>` so the user's global fossil config |
||||
db records nothing (stage-local `_fossil` db, deleted with `clean`). |
||||
|
||||
## Fresh-checkout preparations the driver performs |
||||
|
||||
Normally products of `configure`/`nmake` that a bare checkout lacks (G-076 findings): |
||||
|
||||
- `win/tclUuid.h` - generated from `manifest.uuid`. |
||||
- `win/tclsh.exe.manifest` - generated from its `.in` (`TCL_WIN_VERSION` per the tree's |
||||
own formula: `TCL_VERSION . releaselevel . patchlevel-stripped-of-'ab.'`). |
||||
- `thread/win/thread.rc` - gains `#include "../../tcl905/generic/tcl.h"` (the zig rc |
||||
compile does not receive the makefile's include paths; historically an uncommitted |
||||
tweak in the 2024 checkout, now a scripted idempotent edit of the staged copy). |
||||
|
||||
## Lineage |
||||
|
||||
`build905.zig` descends from the validated ad-hoc builds in `c:/buildtcl/2024zig/build_tcl90` |
||||
(reference snapshot: `TEMP_REFERENCE/2024zig`): `build90.zig` (tcl 9.0.1 era, last |
||||
built 2024-11-27, zig 0.14.0-dev.2074) plus the 2026-07-20 G-076 deltas - source folder |
||||
`../tcl905`, tclUuid.h copy-to-source targets derived from `tcl_source_folder`, and |
||||
`mingw_unicode_entry_point = true` (9.0.5 sources dropped `TCL_BROKEN_MAINARGS`; |
||||
tclAppInit's `_tmain` is `wmain` under `-DUNICODE`). Full provenance: |
||||
`goals/archive/G-076-tcl9-deadconsole-fix-adoption.md` (2026-07-20 entries). Goal |
||||
contract for this suite: `goals/G-096-zig-buildsuite-piperepl.md`. |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,190 @@
|
||||
const std = @import("std"); |
||||
const fs = std.fs; |
||||
|
||||
// Although this function looks imperative, note that its job is to |
||||
// declaratively construct a build graph that will be executed by an external |
||||
// runner. |
||||
//pub fn build(b: *std.Build) !void {} |
||||
pub fn build_libtommath(comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step.Compile { |
||||
const lib = b.addStaticLibrary(.{ |
||||
.name = "tommath", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
|
||||
var flags = std.ArrayList([]const u8).init(b.allocator); |
||||
defer flags.deinit(); |
||||
//try flags.appendSlice(&.{ "-Wall", "-O2" }); |
||||
try flags.appendSlice(&.{ "-Wall", "-O2", "-Wextra", "-Werror=return-type" }); |
||||
//libtom relies on dead code elimination - see s_mp_rand_platform.c |
||||
//The -O2 seems to fix the issue where dead code elimination is not performed (on windows anyway) |
||||
|
||||
var sources = std.ArrayList([]const u8).init(b.allocator); |
||||
{ |
||||
var dir = try fs.cwd().openDir(subdir, .{ .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 sources.append(b.pathJoin(&.{ |
||||
subdir, |
||||
entry.name, |
||||
})); |
||||
} |
||||
} |
||||
//const ext = std.fs.path.extension(entry.basename); |
||||
} |
||||
} |
||||
|
||||
//std.debug.print("sources: {any}",.{sources.items}); |
||||
lib.linkLibC(); |
||||
if (target.result.os.tag == .windows) { |
||||
lib.linkSystemLibrary("advapi32"); //crypt |
||||
} |
||||
//lib.addIncludePath(.{ .path = subdir }); |
||||
lib.addIncludePath(b.path(subdir)); |
||||
lib.addIncludePath(b.path("../tcl90/generic")); //todo - fix |
||||
lib.addCSourceFiles(.{ |
||||
.files = sources.items, |
||||
.flags = flags.items, |
||||
}); |
||||
|
||||
// This declares intent for the library to be installed into the standard |
||||
// location when the user invokes the "install" step (the default step when |
||||
// running `zig build`). |
||||
b.installArtifact(lib); |
||||
|
||||
const exe = b.addExecutable(.{ |
||||
.name = "test_libtommath", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
exe.step.dependOn(&lib.step); |
||||
exe.linkLibC(); |
||||
if (target.result.os.tag == .windows) { |
||||
//exe.linkSystemLibrary("netapi32"); |
||||
//exe.linkSystemLibrary("kernel32"); |
||||
//exe.linkSystemLibrary("user32"); |
||||
exe.linkSystemLibrary("advapi32"); //crypt |
||||
//exe.linkSystemLibrary("userenv"); |
||||
//exe.linkSystemLibrary("ws2_32"); |
||||
} |
||||
//exe.addIncludePath(.{ .path = subdir }); |
||||
exe.addIncludePath(b.path(subdir)); |
||||
//exe.addIncludePath(.{ .path = subdir ++ "/demo" }); |
||||
exe.addIncludePath(b.path(subdir ++ "/demo")); |
||||
exe.addCSourceFile(.{ |
||||
//.file = .{ .path = subdir ++ "/demo/test.c" }, |
||||
.file = b.path(subdir ++ "/demo/test.c"), |
||||
.flags = flags.items, |
||||
}); |
||||
exe.addCSourceFile(.{ |
||||
//.file = .{ .path = subdir ++ "/demo/shared.c" }, |
||||
.file = b.path(subdir ++ "/demo/shared.c"), |
||||
.flags = flags.items, |
||||
}); |
||||
exe.linkLibrary(lib); |
||||
|
||||
// This declares intent for the executable to be installed into the |
||||
// standard location when the user invokes the "install" step (the default |
||||
// step when running `zig build`). |
||||
b.installArtifact(exe); |
||||
|
||||
// This *creates* a Run step in the build graph, to be executed when another |
||||
// step is evaluated that depends on it. The next line below will establish |
||||
// such a dependency. |
||||
const run_cmd = b.addRunArtifact(exe); |
||||
|
||||
if (target.result.os.tag == .windows) { |
||||
try flags.appendSlice(&.{ |
||||
"-Wl,--out-implib=libtommath.dll.a", |
||||
}); |
||||
const dll = b.addSharedLibrary(.{ |
||||
.name = "libtommath", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
dll.linkLibC(); |
||||
dll.linkSystemLibrary("advapi32"); //crypt |
||||
//dll.addIncludePath(.{ .path = subdir }); |
||||
dll.addIncludePath(b.path(subdir)); |
||||
dll.addCSourceFiles(.{ |
||||
.files = sources.items, |
||||
.flags = flags.items, |
||||
}); |
||||
b.installArtifact(dll); |
||||
} else { |
||||
const shlib_tom = b.addSharedLibrary(.{ |
||||
.name = "tommath", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
shlib_tom.linkLibC(); |
||||
//shlib_tom.addIncludePath(.{ .path = subdir }); |
||||
shlib_tom.addIncludePath(b.path(subdir)); |
||||
shlib_tom.addCSourceFiles(.{ |
||||
.files = sources.items, |
||||
.flags = flags.items, |
||||
}); |
||||
b.installArtifact(shlib_tom); |
||||
} |
||||
|
||||
// By making the run step depend on the install step, it will be run from the |
||||
// installation directory rather than directly from within the cache directory. |
||||
// This is not necessary, however, if the application depends on other installed |
||||
// files, this ensures they will be present and in the expected location. |
||||
run_cmd.step.dependOn(b.getInstallStep()); |
||||
|
||||
// This allows the user to pass arguments to the application in the build |
||||
// command itself, like this: `zig build run -- arg1 arg2 etc` |
||||
if (b.args) |args| { |
||||
run_cmd.addArgs(args); |
||||
} |
||||
|
||||
// This creates a build step. It will be visible in the `zig build --help` menu, |
||||
// and can be selected like this: `zig build run` |
||||
// This will evaluate the `run` step rather than the default, which is "install". |
||||
const run_step = b.step("run", "Run the app"); |
||||
run_step.dependOn(&run_cmd.step); |
||||
|
||||
const prepare = b.addSystemCommand(&.{ |
||||
"echo", |
||||
"--system command prepare--", |
||||
}); |
||||
run_cmd.step.dependOn(&prepare.step); |
||||
b.getInstallStep().dependOn(&prepare.step); |
||||
return lib; |
||||
|
||||
// Creates a step for unit testing. This only builds the test executable |
||||
// but does not run it. |
||||
//const lib_unit_tests = b.addTest(.{ |
||||
// //.root_source_file = .{ .path = "src/root.zig" }, |
||||
// .target = target, |
||||
// .optimize = optimize, |
||||
//}); |
||||
|
||||
//const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); |
||||
|
||||
//const exe_unit_tests = b.addTest(.{ |
||||
// .root_source_file = .{ .path = "src/main.zig" }, |
||||
// .target = target, |
||||
// .optimize = optimize, |
||||
//}); |
||||
|
||||
//const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); |
||||
|
||||
// Similar to creating the run step earlier, this exposes a `test` step to |
||||
// the `zig build --help` menu, providing a way for the user to request |
||||
// running the unit tests. |
||||
//const test_step = b.step("test", "Run unit tests"); |
||||
//test_step.dependOn(&run_lib_unit_tests.step); |
||||
//test_step.dependOn(&run_exe_unit_tests.step); |
||||
} |
||||
@ -0,0 +1,152 @@
|
||||
const std = @import("std"); |
||||
const fs = std.fs; |
||||
const builtin = @import("builtin"); |
||||
|
||||
const mkvar_thread_zip_file = "libthread3.0.1.zip"; |
||||
const mkvar_thread_vfs_root = "libthread.vfs"; |
||||
const mkvar_thread_vfs_path = mkvar_thread_vfs_root ++ "/thread_library"; |
||||
|
||||
const mkvar_pkg_lib_file = "tcl9thread301.dll"; |
||||
|
||||
// Although this function looks imperative, note that its job is to |
||||
// declaratively construct a build graph that will be executed by an external |
||||
// runner. |
||||
//pub fn build(b: *std.Build) !void {} |
||||
pub fn build_tclthread(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, stublib: *std.Build.Step.Compile) !*std.Build.Step.Compile { |
||||
const wrapfilecmd = b.addExecutable(.{ |
||||
.name = "wrapfiletofile", |
||||
.root_source_file = b.path("tools/wrapfiletofile.zig"), |
||||
.target = b.host, |
||||
}); |
||||
const uuidcmd_step = b.addRunArtifact(wrapfilecmd); |
||||
uuidcmd_step.addArgs(&.{ |
||||
"-prefix", |
||||
"#define THREAD_VERSION_UUID \\", |
||||
"-prefixnl", |
||||
"1", |
||||
"-input", |
||||
}); |
||||
uuidcmd_step.addFileArg(b.path(subdir ++ "/manifest.uuid")); |
||||
uuidcmd_step.addArg("-output"); |
||||
const threaduuid_file = uuidcmd_step.addOutputFileArg("threadUuid.h"); |
||||
|
||||
//var interp: []const []const u8 = undefined; |
||||
////note we determine the interp based on the machine running the build - not the target |
||||
//if (builtin.os.tag == .windows) { |
||||
// interp = &.{ |
||||
// "wrapfiletofile.cmd", |
||||
// }; |
||||
//} else { |
||||
// interp = &.{ |
||||
// "bash", //explicitly use bash. Sometimes sh is linked to some other shell such as zsh, or dash (e.g debian,ubuntu) which don't understand substitution syntax like bash and ordinary posix sh do |
||||
// "wrapfiletofile.cmd", |
||||
// }; |
||||
//} |
||||
//const uuid_cmd = b.addSystemCommand(interp); |
||||
//uuid_cmd.addArgs(&.{ |
||||
// "-prefix", |
||||
// "#define THREAD_VERSION_UUID \\", |
||||
// "-prefixnl", |
||||
// "1", |
||||
// "-input", |
||||
//}); |
||||
////uuid_cmd.addFileArg(.{ .path = b.pathJoin(&.{ b.pathFromRoot(""), "..", "tcl" ++ tcl_version, "manifest.uuid" }) }); |
||||
//uuid_cmd.addFileArg(b.path(subdir ++ "/manifest.Uuid")); |
||||
//uuid_cmd.addArg("-output"); |
||||
//const tcluuid_file1 = uuid_cmd.addOutputFileArg("threadUuid.h"); |
||||
|
||||
//const wf_uuidh = b.addWriteFiles(); |
||||
const wf_uuidh = b.addUpdateSourceFiles(); |
||||
if (target.result.os.tag == .windows) { |
||||
wf_uuidh.addCopyFileToSource(threaduuid_file, b.pathJoin(&.{ subdir, "win", "threadUuid.h" })); |
||||
} else { |
||||
wf_uuidh.addCopyFileToSource(threaduuid_file, b.pathJoin(&.{ subdir, "unix", "threadUuid.h" })); |
||||
} |
||||
|
||||
wf_uuidh.step.dependOn(&uuidcmd_step.step); |
||||
b.getInstallStep().dependOn(&wf_uuidh.step); |
||||
|
||||
const lib = b.addSharedLibrary(.{ |
||||
.name = "tcl9thread301", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
//lib.step.dependOn(&wf_uuidh.step); |
||||
var flags = std.ArrayList([]const u8).init(b.allocator); |
||||
defer flags.deinit(); |
||||
//try flags.appendSlice(&.{ "-Wall", "-O2", "-fomit-frame-pointer", "-DNDEBUG" }); |
||||
try flags.appendSlice(&.{ "-Wall", "-O3", "-fomit-frame-pointer" }); |
||||
try flags.appendSlice(&.{ |
||||
"-DPACKAGE_NAME=\"thread\"", |
||||
"-DPACKAGE_TARNAME=\"thread\"", |
||||
"-DPACKAGE_VERSION=\"3.0.1\"", |
||||
"-DPACKAGE_STRING=\"thread 3.0.1\"", |
||||
"-DPACKAGE_BUGREPORT=\"\"", |
||||
"-DPACKAGE_URL=\"\"", |
||||
"-DTCL_CFG_OPTIMIZED=1", |
||||
"-DUSE_TCL_STUBS=1", |
||||
"-DZIPFS_BUILD=1", |
||||
"-DTCL_WIDE_INT_TYPE=long long", |
||||
//"-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_FILE_OFFSET_BITS=64", |
||||
//"-DHAVE_STRUCT_STAT64=1", |
||||
//"-DHAVE_LSEEK64=1", |
||||
|
||||
"-static-libgcc", |
||||
}); |
||||
|
||||
//BUILD_<somelibrary> needs to be defined for windows |
||||
if (target.result.os.tag == .windows) { |
||||
try flags.appendSlice(&.{ |
||||
"-DBUILD_thread=/**/", |
||||
}); |
||||
} |
||||
|
||||
var tclthread_sources = std.ArrayList([]const u8).init(b.allocator); |
||||
try tclthread_sources.append(subdir ++ "/generic/threadNs.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/threadCmd.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/threadSvCmd.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/threadSpCmd.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/threadPoolCmd.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/psGdbm.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/psLmdb.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/threadSvListCmd.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/threadSvKeyListCmd.c"); |
||||
try tclthread_sources.append(subdir ++ "/generic/tclXkeylist.c"); |
||||
|
||||
//if (target.result.os.tag == .windows) { |
||||
// lib.linkSystemLibrary("advapi32"); //crypt |
||||
//} |
||||
lib.addIncludePath(b.path(subdir)); |
||||
lib.addIncludePath(b.path(subdir ++ "/generic")); |
||||
//generated threadUuid.h: current thread trunk #includes it (upstream makefiles |
||||
//generate it into their build dir); using the generator's output dir as an include |
||||
//path also gives the compile its dependency on the generation step. |
||||
lib.addIncludePath(threaduuid_file.dirname()); |
||||
lib.addIncludePath(b.path(tcldir ++ "/generic")); //todo - fix |
||||
lib.addCSourceFiles(.{ |
||||
.files = tclthread_sources.items, |
||||
.flags = flags.items, |
||||
}); |
||||
|
||||
lib.linkLibrary(stublib); |
||||
lib.linkLibC(); |
||||
lib.addWin32ResourceFile(.{ |
||||
.file = b.path(subdir ++ "/win/thread.rc"), |
||||
.flags = &.{ |
||||
"-DPACKAGE_MAJOR=3", |
||||
"-DPACKAGE_MINOR=0", |
||||
"-DPACKAGE_VERSION=3.0b2", |
||||
}, |
||||
}); //ignored for non-windows targets |
||||
b.installArtifact(lib); |
||||
|
||||
return lib; |
||||
} |
||||
@ -0,0 +1,81 @@
|
||||
const std = @import("std"); |
||||
const fs = std.fs; |
||||
|
||||
const tclvfs_dotversion = "1.4.2"; |
||||
const tclvfs_nodotversion = "142"; |
||||
|
||||
pub fn vfsadd_tclvfs_files(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, vfswrite: *std.Build.Step.WriteFile) !*std.Build.Step.WriteFile { |
||||
//_ = tcldir; |
||||
//const version = "1.4.2"; |
||||
//recursive copy |
||||
//_ = make_vfs_root.addCopyDirectory(b.path(tcl_source_folder ++ "/../tclvfs/library"), "base/lib/vfs1.4.2", .{}); |
||||
_ = vfswrite.addCopyDirectory(b.path(tcldir ++ "/" ++ subdir ++ "/library"), b.pathJoin(&.{ "base", "lib", "vfs" ++ tclvfs_dotversion }), .{}); |
||||
|
||||
return vfswrite; |
||||
} |
||||
pub fn build_tclvfs(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, stublib: *std.Build.Step.Compile) !*std.Build.Step.Compile { |
||||
//review - libname Vfs vs vfs??? |
||||
const lib = b.addSharedLibrary(.{ |
||||
.name = "tcl9vfs142", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
//_ = stublib; |
||||
var flags = std.ArrayList([]const u8).init(b.allocator); |
||||
defer flags.deinit(); |
||||
//try flags.appendSlice(&.{ "-Wall", "-O3" }); |
||||
try flags.appendSlice(&.{ "-Wall", "-O3", "-fomit-frame-pointer" }); |
||||
try flags.appendSlice(&.{ |
||||
"-DPACKAGE_NAME=\"vfs\"", |
||||
"-DPACKAGE_TARNAME=\"vfs\"", |
||||
"-DPACKAGE_VERSION=\"" ++ tclvfs_dotversion ++ "\"", |
||||
"-DPACKAGE_STRING=\"vfs " ++ tclvfs_dotversion ++ "\"", |
||||
"-DPACKAGE_BUGREPORT=\"\"", |
||||
"-DPACKAGE_URL=\"\"", |
||||
"-DBUILD_VFS=1", |
||||
"-DTCL_CFG_OPTIMIZED=1", |
||||
"-DUSE_TCL_STUBS=1", |
||||
"-DZIPFS_BUILD=1", |
||||
"-DTCL_WIDE_INT_TYPE=long long", |
||||
}); |
||||
if (target.result.os.tag == .windows) { |
||||
try flags.appendSlice(&.{ |
||||
"-static-libgcc", |
||||
}); |
||||
} |
||||
//-DBUILD_VFS only on windows? value? |
||||
|
||||
var sources = std.ArrayList([]const u8).init(b.allocator); |
||||
try sources.append(subdir ++ "/generic/vfs.c"); |
||||
lib.addIncludePath(b.path(subdir)); |
||||
lib.addIncludePath(b.path(subdir ++ "/generic")); |
||||
lib.addIncludePath(b.path(tcldir ++ "/generic")); |
||||
lib.addIncludePath(b.path(tcldir ++ "/generic")); |
||||
if (target.result.os.tag == .windows) { |
||||
lib.addIncludePath(b.path(tcldir ++ "/win")); |
||||
} else { |
||||
lib.addIncludePath(b.path(tcldir ++ "/unix")); |
||||
} |
||||
lib.addCSourceFiles(.{ |
||||
.files = sources.items, |
||||
.flags = flags.items, |
||||
}); |
||||
|
||||
lib.linkLibrary(stublib); |
||||
lib.linkLibC(); |
||||
lib.addWin32ResourceFile(.{ |
||||
.file = b.path(subdir ++ "/win/tclvfs.rc"), |
||||
.flags = &.{ |
||||
"-DPACKAGE_MAJOR=1", |
||||
"-DPACKAGE_MINOR=4", |
||||
"-DPACKAGE_VERSION=" ++ tclvfs_dotversion, |
||||
"-DDOTVERSION=" ++ tclvfs_dotversion, |
||||
"-DCOMMAVERSION=" ++ tclvfs_dotversion, |
||||
"-DVERSION=" ++ tclvfs_nodotversion, |
||||
}, |
||||
}); //ignored for non-windows targets |
||||
// COMMAVERSION??? VERSION??? |
||||
|
||||
b.installArtifact(lib); |
||||
return lib; //static library |
||||
} |
||||
@ -0,0 +1,76 @@
|
||||
const std = @import("std"); |
||||
const fs = std.fs; |
||||
|
||||
const tclvfs_dotversion = "1.4.2"; |
||||
const tclvfs_nodotversion = "142"; |
||||
|
||||
pub fn vfsadd_tclvfs_files(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, vfswrite: *std.Build.Step.WriteFile) !*std.Build.Step.WriteFile { |
||||
//_ = tcldir; |
||||
//const version = "1.4.2"; |
||||
//recursive copy |
||||
//_ = make_vfs_root.addCopyDirectory(b.path(tcl_source_folder ++ "/../tclvfs/library"), "base/lib/vfs1.4.2", .{}); |
||||
_ = vfswrite.addCopyDirectory(b.path(tcldir ++ "/" ++ subdir ++ "/library"), b.pathJoin(&.{ "base", "lib", "vfs" ++ tclvfs_dotversion }), .{}); |
||||
|
||||
return vfswrite; |
||||
} |
||||
pub fn build_tclvfs(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, stublib: *std.Build.Step.Compile) !*std.Build.Step.Compile { |
||||
//review - libname Vfs vs vfs??? |
||||
const slib = b.addStaticLibrary(.{ |
||||
.name = "Vfs", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
|
||||
_ = stublib; |
||||
var flags = std.ArrayList([]const u8).init(b.allocator); |
||||
defer flags.deinit(); |
||||
//try flags.appendSlice(&.{ "-Wall", "-O3" }); |
||||
try flags.appendSlice(&.{ "-Wall", "-O2", "-fomit-frame-pointer" }); |
||||
try flags.appendSlice(&.{ |
||||
"-DPACKAGE_NAME=\"vfs\"", |
||||
"-DPACKAGE_TARNAME=\"vfs\"", |
||||
"-DPACKAGE_VERSION=\"" ++ tclvfs_dotversion ++ "\"", |
||||
"-DPACKAGE_STRING=\"vfs " ++ tclvfs_dotversion ++ "\"", |
||||
"-DPACKAGE_BUGREPORT=\"\"", |
||||
"-DPACKAGE_URL=\"\"", |
||||
"-DBUILD_VFS=1", |
||||
"-DTCL_CFG_OPTIMIZED=1", |
||||
"-DUSE_TCL_STUBS=0", |
||||
"-DZIPFS_BUILD=1", |
||||
"-DTCL_WIDE_INT_TYPE=long long", |
||||
"-DSTATIC_BUILD", |
||||
// ... |
||||
//"-static-libgcc", |
||||
}); |
||||
//-DBUILD_VFS only on windows? value? |
||||
|
||||
var sources = std.ArrayList([]const u8).init(b.allocator); |
||||
try sources.append(subdir ++ "/generic/vfs.c"); |
||||
slib.addIncludePath(b.path(subdir)); |
||||
slib.addIncludePath(b.path(subdir ++ "/generic")); |
||||
slib.addIncludePath(b.path(tcldir ++ "/generic")); |
||||
slib.addIncludePath(b.path(tcldir ++ "/generic")); |
||||
slib.addIncludePath(b.path(tcldir ++ "/win")); |
||||
slib.addCSourceFiles(.{ |
||||
.files = sources.items, |
||||
.flags = flags.items, |
||||
}); |
||||
|
||||
//slib.linkLibrary(stublib); |
||||
slib.linkLibC(); |
||||
slib.addWin32ResourceFile(.{ |
||||
.file = b.path(subdir ++ "/win/tclvfs.rc"), |
||||
.flags = &.{ |
||||
"-DPACKAGE_MAJOR=1", |
||||
"-DPACKAGE_MINOR=4", |
||||
"-DPACKAGE_VERSION=" ++ tclvfs_dotversion, |
||||
"-DDOTVERSION=" ++ tclvfs_dotversion, |
||||
"-DCOMMAVERSION=" ++ tclvfs_dotversion, |
||||
"-DVERSION=" ++ tclvfs_nodotversion, |
||||
}, |
||||
}); //ignored for non-windows targets |
||||
// COMMAVERSION??? VERSION??? |
||||
|
||||
//b.installArtifact(slib); |
||||
return slib; //static library |
||||
} |
||||
@ -0,0 +1,80 @@
|
||||
const std = @import("std"); |
||||
const fs = std.fs; |
||||
|
||||
const vqtcl_dotversion = "4.1"; |
||||
const vqtcl_nodotversion = "41"; |
||||
|
||||
pub fn vfsadd_vqtcl_files(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, vfswrite: *std.Build.Step.WriteFile) !*std.Build.Step.WriteFile { |
||||
//_ = tcldir; |
||||
//recursive copy |
||||
//_ = make_vfs_root.addCopyDirectory(b.path(tcl_source_folder ++ "/../tclvfs/library"), "base/lib/vfs1.4.2", .{}); |
||||
_ = vfswrite.addCopyDirectory(b.path(tcldir ++ "/" ++ subdir ++ "/library"), b.pathJoin(&.{ "base", "lib", "vqtcl" ++ vqtcl_dotversion }), .{}); |
||||
|
||||
return vfswrite; |
||||
} |
||||
pub fn build_vqtcl(comptime tcldir: []const u8, comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, stublib: *std.Build.Step.Compile) !*std.Build.Step.Compile { |
||||
const lib = b.addSharedLibrary(.{ |
||||
.name = "tcl9vlerq" ++ vqtcl_nodotversion, |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
//_ = stublib; |
||||
var flags = std.ArrayList([]const u8).init(b.allocator); |
||||
defer flags.deinit(); |
||||
//try flags.appendSlice(&.{ "-Wall", "-O3" }); |
||||
//try flags.appendSlice(&.{ "-Wall", "-O2", "-fomit-frame-pointer" }); |
||||
try flags.appendSlice(&.{ "-O3", "-fomit-frame-pointer" }); |
||||
try flags.appendSlice(&.{ |
||||
"-DPACKAGE_NAME=\"vfs\"", |
||||
"-DPACKAGE_TARNAME=\"vfs\"", |
||||
"-DPACKAGE_VERSION=\"" ++ vqtcl_dotversion ++ "\"", |
||||
"-DPACKAGE_STRING=\"vfs " ++ vqtcl_dotversion ++ "\"", |
||||
"-DPACKAGE_BUGREPORT=\"\"", |
||||
"-DPACKAGE_URL=\"\"", |
||||
"-DTCL_CFG_OPTIMIZED=1", |
||||
"-DUSE_TCL_STUBS=1", |
||||
"-DZIPFS_BUILD=1", |
||||
"-DTCL_WIDE_INT_TYPE=long long", |
||||
}); |
||||
// "-DTCL_MEM_DEBUG=1", //define for all or none of modules that are linked together |
||||
|
||||
// "-DTCL_MAJOR_VERSION=9", |
||||
//"-static-libgcc", //argument unused - review |
||||
|
||||
var sources = std.ArrayList([]const u8).init(b.allocator); |
||||
try sources.append(subdir ++ "/generic/vlerq.c"); |
||||
lib.addIncludePath(b.path(subdir)); |
||||
lib.addIncludePath(b.path(subdir ++ "/generic")); |
||||
lib.addIncludePath(b.path(subdir ++ "/win")); //header for MSVC only - possibly not needed here - unless zig build can use it? |
||||
lib.addIncludePath(b.path(tcldir ++ "/generic")); |
||||
lib.addIncludePath(b.path(tcldir ++ "/generic")); |
||||
if (target.result.os.tag == .windows) { |
||||
lib.addIncludePath(b.path(tcldir ++ "/win")); |
||||
} else { |
||||
lib.addIncludePath(b.path(tcldir ++ "/unix")); |
||||
} |
||||
lib.addCSourceFiles(.{ |
||||
.files = sources.items, |
||||
.flags = flags.items, |
||||
}); |
||||
|
||||
lib.linkLibrary(stublib); |
||||
lib.linkLibC(); |
||||
|
||||
// no resource file - todo - make one? |
||||
//lib.addWin32ResourceFile(.{ |
||||
// .file = b.path(subdir ++ "/win/tclvfs.rc"), |
||||
// .flags = &.{ |
||||
// "-DPACKAGE_MAJOR=1", |
||||
// "-DPACKAGE_MINOR=4", |
||||
// "-DPACKAGE_VERSION=" ++ vqtcl_dotversion, |
||||
// "-DDOTVERSION=" ++ vqtcl_dotversion, |
||||
// "-DCOMMAVERSION=" ++ vqtcl_dotversion, |
||||
// "-DVERSION=" ++ vqtcl_nodotversion, |
||||
// }, |
||||
//}); //ignored for non-windows targets |
||||
// COMMAVERSION??? VERSION??? |
||||
|
||||
b.installArtifact(lib); |
||||
return lib; //static library |
||||
} |
||||
@ -0,0 +1,161 @@
|
||||
const std = @import("std"); |
||||
const fs = std.fs; |
||||
|
||||
pub fn build_zlib(comptime subdir: []const u8, b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step.Compile { |
||||
var statlibname: []const u8 = undefined; |
||||
if (target.result.os.tag == .windows) { |
||||
statlibname = "libz"; |
||||
} else { |
||||
statlibname = "z"; |
||||
} |
||||
|
||||
const statlib = b.addStaticLibrary(.{ |
||||
.name = statlibname, |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
var zlib_flags = std.ArrayList([]const u8).init(b.allocator); |
||||
defer zlib_flags.deinit(); |
||||
//try zlib_flags.appendSlice(&.{ "-Wall", "-static-libgcc", "-Wno-error=implicit-function-declaration", "-O3" }); //DL_info undeclared |
||||
//try zlib_flags.appendSlice(&.{ "-Wall", "-static-libgcc", "-O3" }); //DL_info undeclared |
||||
//try zlib_flags.appendSlice(&.{ "-Wall", "-Wno-error=implicit-function-declaration", "-O3", "-fPIC" }); //seems to work with linux-musl |
||||
//try zlib_flags.appendSlice(&.{ "-Wall", "-Wno-error=implicit-function-declaration", "-O3", "-fPIC", "-fno-omit-frame-pointer", "-fsanitize=undefined" }); |
||||
|
||||
//2024 - seems to work |
||||
try zlib_flags.appendSlice(&.{ "-Wall", "-Wno-error=implicit-function-declaration", "-O3", "-fPIC", "-fno-omit-frame-pointer", "-fno-sanitize=undefined" }); |
||||
|
||||
//2024 trial |
||||
//try zlib_flags.appendSlice(&.{ "-Wall", "-Wno-error=implicit-function-declaration", "-O2", "-fPIC", "-fomit-frame-pointer", "-fno-sanitize=undefined" }); |
||||
//try zlib_flags.appendSlice(&.{ "-Wall", "-Wno-error=implicit-function-declaration", "-fPIC", "-fomit-frame-pointer", "-fno-sanitize=undefined" }); |
||||
|
||||
//try zlib_flags.appendSlice(&.{ "-Wall", "-O3" }); |
||||
//try zlib_flags.appendSlice(&.{"-O"}); //C99 and later do not support implicit function declarations |
||||
//try zlib_flags.appendSlice(&.{ "-O3", "-Wall", "-Wwrite-strings", "-Wpointer-arith", "-Wconversion", "-Wstrict-prototypes", "-Wmissing-prototypes" }); |
||||
|
||||
var zlib_sources = std.ArrayList([]const u8).init(b.allocator); |
||||
{ |
||||
var dir = try fs.cwd().openDir(subdir, .{ .iterate = true }); |
||||
defer dir.close(); |
||||
|
||||
//var walker = try dir.walk(b.allocator); |
||||
//defer walker.deinit(); |
||||
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 zlib_sources.append(b.pathJoin(&.{ |
||||
subdir, |
||||
entry.name, |
||||
})); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
statlib.linkLibC(); |
||||
statlib.addIncludePath(b.path(subdir)); |
||||
statlib.addIncludePath(b.path(subdir ++ "/contrib/minzip")); |
||||
statlib.addCSourceFiles(.{ |
||||
.files = zlib_sources.items, |
||||
.flags = zlib_flags.items, |
||||
}); |
||||
b.installArtifact(statlib); |
||||
var dylib_flags = std.ArrayList([]const u8).init(b.allocator); |
||||
try dylib_flags.appendSlice(zlib_flags.items); |
||||
|
||||
//trial |
||||
//try dylib_flags.appendSlice(&.{ |
||||
// "-DZLIB_DLL", |
||||
//}); |
||||
|
||||
//"-Wl,-with-default-msvcrt=ucrtbase", //no effect - seems to be unnecessary anyway - ldd does show msvcrt as well as ucrtbase, but shims seem to all use ucrtbase.dll |
||||
//"-Wl,-out-implib,libz.dll.a", //seems to have no effect. zig always creates a .lib |
||||
//"-DZLIB_WINAPI", |
||||
//"-Wl,-exclude-libs=ALL", |
||||
std.debug.print("build_zlib.zig flags {s}\n", .{dylib_flags.items}); |
||||
const dylib = b.addSharedLibrary(.{ |
||||
.name = "zlib1", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
dylib.linkLibC(); |
||||
dylib.addIncludePath(b.path(subdir)); |
||||
dylib.addIncludePath(b.path(subdir ++ "/contrib/minizip")); |
||||
dylib.addCSourceFiles(.{ |
||||
.files = zlib_sources.items, |
||||
.flags = dylib_flags.items, |
||||
}); |
||||
//dylib.addWin32ResourceFile(.{ .file = .{ .path = "zlib/win32/zlib1.rc" } }); //ignored for non-windows targets |
||||
dylib.addWin32ResourceFile(.{ .file = b.path(subdir ++ "/win32/zlib1.rc") }); //ignored for non-windows targets |
||||
b.installArtifact(dylib); |
||||
|
||||
const mgz_exe = b.addExecutable(.{ |
||||
.name = "minigzip", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
mgz_exe.step.dependOn(&statlib.step); |
||||
mgz_exe.addIncludePath(b.path(subdir)); |
||||
mgz_exe.addCSourceFile(.{ |
||||
.file = b.path(subdir ++ "/test/minigzip.c"), |
||||
.flags = zlib_flags.items, |
||||
}); |
||||
mgz_exe.linkLibC(); |
||||
mgz_exe.linkLibrary(statlib); |
||||
b.installArtifact(mgz_exe); |
||||
|
||||
const eg_exe = b.addExecutable(.{ |
||||
.name = "zlib_example", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
eg_exe.step.dependOn(&statlib.step); |
||||
eg_exe.addIncludePath(b.path(subdir)); |
||||
eg_exe.addCSourceFile(.{ |
||||
.file = b.path(subdir ++ "/test/example.c"), |
||||
.flags = zlib_flags.items, |
||||
}); |
||||
eg_exe.linkLibC(); |
||||
eg_exe.linkLibrary(statlib); |
||||
b.installArtifact(eg_exe); |
||||
|
||||
const egd_exe = b.addExecutable(.{ |
||||
.name = "zlib_example_d", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
egd_exe.step.dependOn(&statlib.step); |
||||
egd_exe.addIncludePath(b.path(subdir)); |
||||
egd_exe.addCSourceFile(.{ |
||||
.file = b.path(subdir ++ "/test/example.c"), |
||||
.flags = dylib_flags.items, |
||||
}); |
||||
egd_exe.linkLibrary(dylib); |
||||
egd_exe.root_module.addRPathSpecial("$ORIGIN/../lib"); |
||||
egd_exe.linkLibC(); |
||||
b.installArtifact(egd_exe); |
||||
|
||||
const mgzd_exe = b.addExecutable(.{ |
||||
.name = "minigzip_d", |
||||
.target = target, |
||||
.optimize = optimize, |
||||
}); |
||||
mgzd_exe.step.dependOn(&statlib.step); |
||||
mgzd_exe.addIncludePath(b.path(subdir)); |
||||
mgzd_exe.addCSourceFile(.{ |
||||
.file = b.path(subdir ++ "/test/minigzip.c"), |
||||
.flags = dylib_flags.items, |
||||
}); |
||||
mgzd_exe.root_module.addRPathSpecial("$ORIGIN/../lib"); |
||||
mgzd_exe.linkLibrary(dylib); |
||||
b.installArtifact(mgzd_exe); |
||||
|
||||
return statlib; |
||||
} |
||||
@ -0,0 +1,41 @@
|
||||
# Recovered 2024 Tcl source patches (G-096) |
||||
|
||||
Origin: `c:/buildtcl/2024zig/build_tcl90/patch/` (reference snapshot |
||||
`TEMP_REFERENCE/2024zig/build_tcl90/patch/`), written against ~Tcl 9.0b4-era sources |
||||
(Aug 2024; tclZipfs/tclCmdIL Nov 2024). Recovered into tracked form 2026-07-20 - these |
||||
files previously existed only in those two VCS-ignored on-disk copies. |
||||
|
||||
NONE of these are applied by the suite's default build: `suite.tcl build` compiles |
||||
stock upstream sources. Rebasing/applying is per-patch opt-in work. |
||||
|
||||
## tclMain.c.piperepl.patch / tclMain_piperepl.c (the TCLSH_PIPEREPL capability) |
||||
|
||||
`tclMain_piperepl.c` is the full patched file; the `.patch` is its diff against the |
||||
era's stock `tclMain.c`. |
||||
|
||||
Purpose (the G-096 goal capability): allow returning to a standard C-level tcl repl |
||||
after launching from a kit with its own `main.tcl` - not possible with unmodified |
||||
sources. Gated by the `TCLSH_PIPEREPL` env var (unset/`0` = stock behaviour). When |
||||
enabled: |
||||
|
||||
- Publishes `::tclsh(istty)` (unlinked fact) plus linked `::tclsh(dorepl)` and |
||||
`::tclsh(evalinput)` before the startup script runs; a script-arg launch defaults |
||||
`dorepl` to 0 (stock semantics) but the script may set `::tclsh(dorepl) 1` to drop |
||||
into the interactive repl after it completes. |
||||
- Piped (non-tty) input the script did not consume is accumulated into |
||||
`::tclsh(inputbuffer)` rather than evaluated as commands, unless the script opts in |
||||
with `::tclsh(evalinput) 1` - protects arbitrary piped data from accidental eval. |
||||
- On stdin eof with `dorepl` set, stdin is reopened from the console (`CONIN$` on |
||||
windows, `/dev/tty` elsewhere) and installed as the std channel, yielding a live |
||||
standard repl after a pipeline ends. |
||||
|
||||
Status: NOT yet rebased onto current Tcl 9 sources. Note the punkshell-level analogue |
||||
for the punk repl is G-038 (piped-to-interactive session continuity) - this patch is |
||||
the plain-tclsh/kit-runtime counterpart. |
||||
|
||||
## tclZipfs.patch, tclCmdIL.patch |
||||
|
||||
Companion experiment patches from the same era (zipfs behaviour and tclCmdIL tweak), |
||||
recovered for durability; unexamined in detail. See the fossil-era notes in |
||||
`TEMP_REFERENCE/2024zig/build_tcl90/patch/` (doc/, prev/, test_zipfs_patch/) for their |
||||
surrounding experiment context. |
||||
@ -0,0 +1,13 @@
|
||||
--- tclCmdIL_orig.c Sat Oct 05 06:09:52 2024
|
||||
+++ tclCmdIL.c Mon Nov 18 13:59:01 2024
|
||||
@@ -3926,6 +3926,10 @@
|
||||
itemPtr = SelectObjFromSublist(listv[i+groupOffset],
|
||||
&sortInfo);
|
||||
Tcl_ListObjAppendElement(interp, listPtr, itemPtr);
|
||||
+ } else if (returnSubindices && (sortInfo.indexc == 0) && (groupSize > 1)) {
|
||||
+ Tcl_BounceRefCount(itemPtr);
|
||||
+ itemPtr = listv[i + groupOffset];
|
||||
+ Tcl_ListObjAppendElement(interp, listPtr, itemPtr);
|
||||
} else if (groupSize > 1) {
|
||||
Tcl_ListObjReplace(interp, listPtr, LIST_MAX, 0,
|
||||
groupSize, &listv[i]);
|
||||
@ -0,0 +1,233 @@
|
||||
--- tclMain.c Fri Aug 16 04:10:58 2024
|
||||
+++ tclMain_piperepl.c Mon Aug 19 01:24:03 2024
|
||||
@@ -105,6 +105,22 @@
|
||||
* commands. */
|
||||
} InteractiveState;
|
||||
|
||||
+typedef struct {
|
||||
+ int istty; /* Non-zero means standard input is a
|
||||
+ * terminal-like device
|
||||
+ * Unlike InteractiveState.tty this is
|
||||
+ * unaffected by whether a script path
|
||||
+ * was specified at argv0 */
|
||||
+ int dorepl; /* whether to run the default repl
|
||||
+ * linked to ::tclsh(dorepl)
|
||||
+ * so script file or stdin script
|
||||
+ * can influence */
|
||||
+ int reopened; /* if 1, we are processing from console - not piped input*/
|
||||
+ int evalinput; /* default 0 - whether to evaluate piped input as commands
|
||||
+ * This is input the script (if any) didn't consume
|
||||
+ */
|
||||
+ Tcl_Obj *inputbuffer;
|
||||
+} PipeRepl;
|
||||
/*
|
||||
* Forward declarations for functions defined later in this file.
|
||||
*/
|
||||
@@ -291,6 +307,10 @@
|
||||
Tcl_MainLoopProc *mainLoopProc;
|
||||
Tcl_Channel chan;
|
||||
InteractiveState is;
|
||||
+ PipeRepl piperepl;
|
||||
+ Tcl_Obj *tclInteractive; //JMN
|
||||
+ int ttyAtStart; // Whether we initially had piped data or not
|
||||
+ int inputReopen; // flag for reopening stdin
|
||||
|
||||
TclpSetInitialEncodings();
|
||||
if (argc > 0) {
|
||||
@@ -359,6 +379,37 @@
|
||||
*/
|
||||
|
||||
is.tty = isatty(0);
|
||||
+ ttyAtStart = is.tty; //JMN
|
||||
+ int hasPipeRepl; //flag for whether piperepl and ::tclsh() behaviour enabled
|
||||
+ {
|
||||
+ const char *s;
|
||||
+ Tcl_DString ds;
|
||||
+ s = TclGetEnv("TCLSH_PIPEREPL", &ds);
|
||||
+ hasPipeRepl = ((s != NULL) && strcmp(s, "0"));
|
||||
+ if (s != NULL) {
|
||||
+ Tcl_DStringFree(&ds);
|
||||
+ }
|
||||
+ }
|
||||
+ piperepl.reopened = 0;
|
||||
+ piperepl.dorepl = 0;
|
||||
+ piperepl.evalinput = 0;
|
||||
+ if (hasPipeRepl) {
|
||||
+ TclNewObj(piperepl.inputbuffer);
|
||||
+ piperepl.istty = ttyAtStart;
|
||||
+ piperepl.dorepl = ttyAtStart;
|
||||
+ //unlinked - setting from script should have no effect
|
||||
+ Tcl_SetVar2Ex(interp, "tclsh", "istty",
|
||||
+ Tcl_NewBooleanObj(ttyAtStart), TCL_GLOBAL_ONLY);
|
||||
+ //will be linked so script/stdin can influence
|
||||
+ // if path (script) supplied, but no stdin
|
||||
+ // - will be set 0 to match default 'tclsh <script>' behaviour
|
||||
+ // when ::tclsh(dorepl) not set in the script
|
||||
+ Tcl_SetVar2Ex(interp, "tclsh", "dorepl",
|
||||
+ Tcl_NewBooleanObj(ttyAtStart), TCL_GLOBAL_ONLY);
|
||||
+ Tcl_SetVar2Ex(interp, "tclsh", "evalinput",
|
||||
+ Tcl_NewBooleanObj(piperepl.evalinput), TCL_GLOBAL_ONLY);
|
||||
+ }
|
||||
+
|
||||
Tcl_SetVar2Ex(interp, "tcl_interactive", NULL,
|
||||
Tcl_NewBooleanObj(!path && is.tty), TCL_GLOBAL_ONLY);
|
||||
|
||||
@@ -397,9 +448,18 @@
|
||||
* Invoke the script specified on the command line, if any. Must fetch it
|
||||
* again, as the appInitProc might have reset it.
|
||||
*/
|
||||
-
|
||||
+ if (hasPipeRepl) {
|
||||
+ //REVIEW - ok to use Tcl_LinkVar on individual array elements? (we don't want to Tcl_LinkArray the whole thing)
|
||||
+ Tcl_LinkVar(interp, "tclsh(dorepl)", &piperepl.dorepl, TCL_LINK_BOOLEAN);
|
||||
+ Tcl_LinkVar(interp, "tclsh(evalinput)", &piperepl.evalinput, TCL_LINK_BOOLEAN);
|
||||
+ Tcl_LinkVar(interp, "tcl_interactive", &is.tty, TCL_LINK_BOOLEAN); // link before script only when TCLSH_PIPEREPL true
|
||||
+ }
|
||||
path = Tcl_GetStartupScript(&encodingName);
|
||||
if (path != NULL) {
|
||||
+ if (hasPipeRepl) {
|
||||
+ piperepl.dorepl = 0; //default to standard behaviour of no repl when script arg present - script can override
|
||||
+ // stdin can only override dorepl, tcl_interactive if evalinput set to true in script
|
||||
+ }
|
||||
Tcl_ResetResult(interp);
|
||||
code = Tcl_FSEvalFileEx(interp, path, encodingName);
|
||||
if (code != TCL_OK) {
|
||||
@@ -419,7 +479,35 @@
|
||||
}
|
||||
exitCode = 1;
|
||||
}
|
||||
- goto done;
|
||||
+ if (hasPipeRepl) {
|
||||
+ if (!(piperepl.dorepl || piperepl.evalinput)) {
|
||||
+ //tty or not - we need some reason to progress
|
||||
+ goto done;
|
||||
+ }
|
||||
+ //evalinput may be specified when tty - but in that case there can be no pipeline input
|
||||
+ //This is for usecase to allow pipeline to set dorepl without modifiying script
|
||||
+ if (piperepl.istty && !piperepl.dorepl) {
|
||||
+ //Launch without pipeline -
|
||||
+ goto done;
|
||||
+ }
|
||||
+ } else {
|
||||
+ goto done;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ if (hasPipeRepl) {
|
||||
+ if (path == NULL) {
|
||||
+ piperepl.evalinput = 1; //duplicate standard tclsh behaviour and pass through to repl for eval
|
||||
+ }
|
||||
+ piperepl.inputbuffer = Tcl_NewStringObj("", -1);
|
||||
+ Tcl_IncrRefCount(piperepl.inputbuffer);
|
||||
+ if (piperepl.istty) {
|
||||
+ inputReopen = 0; //no reopen required - eof is from console?
|
||||
+ } else {
|
||||
+ //assume there was some data piped?
|
||||
+ inputReopen = 1;
|
||||
+ }
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -444,7 +532,9 @@
|
||||
* Get a new value for tty if anyone writes to ::tcl_interactive
|
||||
*/
|
||||
|
||||
- Tcl_LinkVar(interp, "tcl_interactive", &is.tty, TCL_LINK_BOOLEAN);
|
||||
+ if (!hasPipeRepl) {
|
||||
+ Tcl_LinkVar(interp, "tcl_interactive", &is.tty, TCL_LINK_BOOLEAN);
|
||||
+ }
|
||||
is.input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
while ((is.input != NULL) && !Tcl_InterpDeleted(interp)) {
|
||||
mainLoopProc = TclGetMainLoop();
|
||||
@@ -464,12 +554,34 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
- if (Tcl_IsShared(is.commandPtr)) {
|
||||
- Tcl_DecrRefCount(is.commandPtr);
|
||||
- is.commandPtr = Tcl_DuplicateObj(is.commandPtr);
|
||||
- Tcl_IncrRefCount(is.commandPtr);
|
||||
+ if (hasPipeRepl && !piperepl.istty && !piperepl.reopened && !piperepl.evalinput) {
|
||||
+ /* still handling from pipe
|
||||
+ * This is data the script (if any) didn't read
|
||||
+ * We should only evaluate as commands if we were explicitly instructed to (::tclsh(evalinput) == 1)
|
||||
+ * Otherwise a script which processes arbitrary data (but set ::tclsh(dorepl) to 1 to drop into a shell)
|
||||
+ * may cause the arbitrary data to be evaled unexpectedly if it failed to process all the input */
|
||||
+ if (Tcl_IsShared(piperepl.inputbuffer)) {
|
||||
+ Tcl_DecrRefCount(piperepl.inputbuffer);
|
||||
+ piperepl.inputbuffer = Tcl_DuplicateObj(piperepl.inputbuffer);
|
||||
+ Tcl_IncrRefCount(piperepl.inputbuffer);
|
||||
+ }
|
||||
+ length = Tcl_GetsObj(is.input,piperepl.inputbuffer);
|
||||
+ if (length >= 0) {
|
||||
+ if (Tcl_IsShared(piperepl.inputbuffer)) {
|
||||
+ Tcl_DecrRefCount(piperepl.inputbuffer);
|
||||
+ piperepl.inputbuffer = Tcl_DuplicateObj(piperepl.inputbuffer);
|
||||
+ Tcl_IncrRefCount(piperepl.inputbuffer);
|
||||
+ }
|
||||
+ Tcl_AppendToObj(piperepl.inputbuffer, "\n", 1);
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (Tcl_IsShared(is.commandPtr)) {
|
||||
+ Tcl_DecrRefCount(is.commandPtr);
|
||||
+ is.commandPtr = Tcl_DuplicateObj(is.commandPtr);
|
||||
+ Tcl_IncrRefCount(is.commandPtr);
|
||||
+ }
|
||||
+ length = Tcl_GetsObj(is.input, is.commandPtr);
|
||||
}
|
||||
- length = Tcl_GetsObj(is.input, is.commandPtr);
|
||||
if (length < 0) {
|
||||
if (Tcl_InputBlocked(is.input)) {
|
||||
/*
|
||||
@@ -487,9 +599,48 @@
|
||||
* Either EOF, or an error on stdin; we're done
|
||||
*/
|
||||
|
||||
- break;
|
||||
+ if (piperepl.dorepl) {
|
||||
+ chan = Tcl_GetStdChannel(TCL_STDOUT);
|
||||
+ inputReopen--;
|
||||
+ if (inputReopen >= 0) {
|
||||
+ Tcl_WriteChars(chan, "? reopen stdin from console \n", -1);
|
||||
+ if (is.input != NULL) {
|
||||
+ //Tcl_UnregisterChannel should close stdin without killing console?
|
||||
+ if (Tcl_UnregisterChannel(interp, is.input) != TCL_OK) {
|
||||
+ Tcl_WriteChars(chan, "?after eof/error on stdin, unregister stdin failed\n", -1);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ #ifndef _WIN32
|
||||
+ is.input = Tcl_OpenFileChannel(interp,"/dev/tty", "r", 0644);
|
||||
+ #else
|
||||
+ is.input = Tcl_OpenFileChannel(interp, "CONIN$", "r", 0644); //windows specific
|
||||
+ #endif
|
||||
+ //is.input = Tcl_GetStdChannel(TCL_STDIN);
|
||||
+ if (is.input == NULL) {
|
||||
+ Tcl_WriteChars(chan, "?after eof/error on stdin, reopen stdin from console failed\n", -1);
|
||||
+ break;
|
||||
+ } else {
|
||||
+ piperepl.reopened = 1;
|
||||
+ Tcl_SetVar2Ex(interp, "tclsh", "inputbuffer", piperepl.inputbuffer, TCL_GLOBAL_ONLY);
|
||||
+ Tcl_SetChannelOption(interp, is.input, "-blocking", "1");
|
||||
+ Tcl_SetChannelOption(interp, is.input, "-buffering", "line");
|
||||
+ Tcl_SetStdChannel(is.input,TCL_STDIN);
|
||||
+ Tcl_RegisterChannel(interp, is.input);
|
||||
+ continue;
|
||||
+ }
|
||||
+ } else {
|
||||
+ break;
|
||||
+ }
|
||||
+ } else {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (hasPipeRepl && !piperepl.istty && !piperepl.reopened && !piperepl.evalinput) {
|
||||
+ /* still handling input from pipe and we are not configured to eval the input
|
||||
+ * keep accumulating in piperepl.inputbuffer*/
|
||||
+ continue;
|
||||
}
|
||||
-
|
||||
/*
|
||||
* Add the newline removed by Tcl_GetsObj back to the string. Have
|
||||
* to add it back before testing completeness, because it can make
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,4 @@
|
||||
const std = @import("std"); |
||||
|
||||
pub const _start = void; |
||||
pub const WinMainCRTStartup = void; |
||||
@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env tclsh |
||||
#suite_tcl90 driver (G-096): stage the tracked zig recipe into ../_build/suite_tcl90, |
||||
#arrange sources, generate the configure-produced files fresh checkouts lack, build, |
||||
#and smoke-test the result. |
||||
# |
||||
#Usage: tclsh suite.tcl build ?options? |
||||
# tclsh suite.tcl clean |
||||
#Options: |
||||
# -zig <path> zig executable (default: $env(PUNK_ZIG), else 'zig' on PATH) |
||||
# -optimize <mode> Debug|ReleaseSafe|ReleaseFast|ReleaseSmall (default ReleaseFast) |
||||
# -tclbranch <branch> tcl branch/tag to open (default core-9-0-branch) |
||||
# -refresh <0|1> fossil update existing source checkouts (default 0) |
||||
# -vqtcl-source <dir> local vqtcl source dir (no public upstream confirmed; |
||||
# default C:/buildtcl/2024zig/vqtcl) |
||||
# -steps <list> zig build steps (default {install install-libraries make-zipfs}) |
||||
# |
||||
#The build area lives entirely under src/buildsuites/_build (VCS-ignored via the |
||||
#existing _build globs). Fossil clones are cached in ~/.fossils; checkouts made here |
||||
#use FOSSIL_HOME=<stage> so the user's global fossil config db records nothing. |
||||
|
||||
proc log {msg} {puts stdout "suite_tcl90: $msg"; flush stdout} |
||||
proc fail {msg} {puts stderr "suite_tcl90 ERROR: $msg"; flush stderr; exit 1} |
||||
|
||||
proc run {args} { |
||||
#stream output; abort on nonzero exit |
||||
log "run: $args" |
||||
if {[catch {exec {*}$args >@stdout 2>@stderr} err opts]} { |
||||
set code [dict get $opts -errorcode] |
||||
if {[lindex $code 0] eq "CHILDSTATUS"} { |
||||
fail "command failed (exit [lindex $code 2]): $args" |
||||
} |
||||
fail "command failed: $args\n$err" |
||||
} |
||||
} |
||||
|
||||
set suiteroot [file dirname [file normalize [info script]]] |
||||
set stage [file normalize [file join $suiteroot .. _build suite_tcl90]] |
||||
set fossildir [file normalize [file join $env(HOME) .fossils]] |
||||
|
||||
#-- options --------------------------------------------------------------- |
||||
set action [lindex $argv 0] |
||||
if {$action ni {build clean}} { |
||||
puts stderr "usage: tclsh suite.tcl build|clean ?options? (see header)" |
||||
exit 2 |
||||
} |
||||
array set opt { |
||||
-optimize ReleaseFast |
||||
-tclbranch core-9-0-branch |
||||
-refresh 0 |
||||
-vqtcl-source C:/buildtcl/2024zig/vqtcl |
||||
-steps {install install-libraries make-zipfs} |
||||
} |
||||
set opt(-zig) [expr {[info exists env(PUNK_ZIG)] ? $env(PUNK_ZIG) : "zig"}] |
||||
foreach {k v} [lrange $argv 1 end] { |
||||
if {![info exists opt($k)]} {fail "unknown option '$k'"} |
||||
set opt($k) $v |
||||
} |
||||
|
||||
if {$action eq "clean"} { |
||||
log "removing $stage" |
||||
file delete -force $stage |
||||
exit 0 |
||||
} |
||||
|
||||
#-- fossil environment ---------------------------------------------------- |
||||
#Checkout registrations go to a stage-local config db, not the user's global one |
||||
#(agent-memory: fossil scratchpad registry pollution). Clones are still shared. |
||||
file mkdir $stage |
||||
set env(FOSSIL_HOME) $stage |
||||
file mkdir $fossildir |
||||
|
||||
proc fossil_source {name url branch dir} { |
||||
#ensure ~/.fossils/<name>.fossil exists (clone if needed) and open it at branch in dir |
||||
global fossildir opt |
||||
set repo [file join $fossildir $name.fossil] |
||||
if {![file exists $repo]} { |
||||
log "cloning $url -> $repo" |
||||
run fossil clone $url $repo |
||||
} |
||||
if {![file exists [file join $dir _FOSSIL_]] && ![file exists [file join $dir .fslckout]]} { |
||||
file mkdir $dir |
||||
set savedpwd [pwd] |
||||
cd $dir |
||||
#--nested: the stage lives inside the punkshell checkout tree |
||||
run fossil open $repo $branch --nested |
||||
cd $savedpwd |
||||
} elseif {$opt(-refresh)} { |
||||
set savedpwd [pwd] |
||||
cd $dir |
||||
run fossil update $branch |
||||
cd $savedpwd |
||||
} |
||||
} |
||||
|
||||
#-- stage the recipe ------------------------------------------------------ |
||||
set builddir [file join $stage build] |
||||
log "staging recipe -> $builddir" |
||||
file delete -force $builddir |
||||
file mkdir $builddir |
||||
foreach item {build905.zig build_libtommath build_zlib build_tclvfs build_vqtcl build_tclthread src tools} { |
||||
file copy [file join $suiteroot $item] $builddir |
||||
} |
||||
|
||||
#-- sources --------------------------------------------------------------- |
||||
fossil_source tcl https://core.tcl-lang.org/tcl $opt(-tclbranch) [file join $stage tcl905] |
||||
fossil_source tclthread https://core.tcl-lang.org/thread trunk [file join $stage tclthread] |
||||
fossil_source tclvfs https://core.tcl-lang.org/tclvfs trunk [file join $stage tclvfs] |
||||
|
||||
#zlib: use the tcl tree's own vendored copy (version-matched; includes contrib/minizip) |
||||
set zlibdir [file join $stage zlib] |
||||
if {![file isdirectory $zlibdir]} { |
||||
log "staging zlib from tcl compat/zlib" |
||||
file copy [file join $stage tcl905 compat zlib] $zlibdir |
||||
} |
||||
#vqtcl: local source only (vlerq 4.1 by jcw - no public upstream confirmed) |
||||
set vqdir [file join $stage vqtcl] |
||||
if {![file isdirectory $vqdir]} { |
||||
if {![file isdirectory $opt(-vqtcl-source)]} { |
||||
fail "vqtcl source not found at '$opt(-vqtcl-source)' (set -vqtcl-source)" |
||||
} |
||||
log "staging vqtcl from $opt(-vqtcl-source)" |
||||
file copy $opt(-vqtcl-source) $vqdir |
||||
} |
||||
|
||||
#-- generated files a fresh tcl checkout lacks (normally configure/prepare products) -- |
||||
set tcldir [file join $stage tcl905] |
||||
#tclUuid.h: '#define TCL_VERSION_UUID \' + manifest.uuid content |
||||
set f [open [file join $tcldir manifest.uuid] r]; set uuid [read $f]; close $f |
||||
set f [open [file join $tcldir win tclUuid.h] w] |
||||
fconfigure $f -translation lf |
||||
puts -nonewline $f "#define TCL_VERSION_UUID \\\n$uuid" |
||||
close $f |
||||
#tclsh.exe.manifest from .in: TCL_WIN_VERSION = TCL_VERSION . releaselevel . patchlevel-stripped |
||||
set f [open [file join $tcldir generic tcl.h] r]; set tclh [read $f]; close $f |
||||
if {![regexp {#\s*define\s+TCL_VERSION\s+"([^"]+)"} $tclh -> tclversion]} {fail "TCL_VERSION not found in tcl.h"} |
||||
if {![regexp {#\s*define\s+TCL_PATCH_LEVEL\s+"([^"]+)"} $tclh -> patchlevel]} {fail "TCL_PATCH_LEVEL not found in tcl.h"} |
||||
set rlevel [expr {[string match *a* $patchlevel] ? 0 : ([string match *b* $patchlevel] ? 1 : 2)}] |
||||
set winver "$tclversion.$rlevel.[string map {a {} b {} . {}} $patchlevel]" |
||||
set f [open [file join $tcldir win tclsh.exe.manifest.in] r]; set manifest [read $f]; close $f |
||||
set manifest [string map [list @TCL_WIN_VERSION@ $winver @MACHINE@ AMD64] $manifest] |
||||
set f [open [file join $tcldir win tclsh.exe.manifest] w] |
||||
fconfigure $f -translation lf |
||||
puts -nonewline $f $manifest |
||||
close $f |
||||
log "generated tclUuid.h + tclsh.exe.manifest (TCL_WIN_VERSION $winver, patchlevel $patchlevel)" |
||||
|
||||
#thread.rc: the zig rc compile does not receive the makefile's include paths; give the |
||||
#version resource script the tcl header it needs (idempotent scripted edit of the |
||||
#staged checkout - the historical recipe carried this as an uncommitted tweak). |
||||
set rcfile [file join $stage tclthread win thread.rc] |
||||
set f [open $rcfile r]; set rc [read $f]; close $f |
||||
if {![string match "*tcl905/generic/tcl.h*" $rc]} { |
||||
set rc [string map [list "#include <winver.h>" "#include <winver.h>\n#include \"../../tcl905/generic/tcl.h\""] $rc] |
||||
set f [open $rcfile w]; fconfigure $f -translation lf; puts -nonewline $f $rc; close $f |
||||
log "thread.rc: inserted tcl.h include for the version resource" |
||||
} |
||||
|
||||
#-- build ----------------------------------------------------------------- |
||||
set zigv [exec $opt(-zig) version] |
||||
log "zig: $opt(-zig) ($zigv)" |
||||
set savedpwd [pwd] |
||||
cd $builddir |
||||
run $opt(-zig) build --build-file build905.zig --prefix [file join $stage out] -Doptimize=$opt(-optimize) {*}$opt(-steps) |
||||
cd $savedpwd |
||||
|
||||
#-- smoke ----------------------------------------------------------------- |
||||
set exe [file join $stage out bin tclsh90s.exe] |
||||
set zexe [file join $stage out bin tclsh90szip.exe] |
||||
foreach {label path script} [list \ |
||||
static $exe {puts [info patchlevel]} \ |
||||
zip $zexe {puts "[info patchlevel] tz=[clock format 0 -gmt 0 -format %Z] auto=[llength [info procs auto_load]]"} \ |
||||
] { |
||||
if {![file exists $path]} {fail "$label shell missing: $path"} |
||||
set out [exec $path << "$script; exit"] |
||||
log "$label shell: $out" |
||||
if {![string match "$patchlevel*" $out]} {fail "$label shell patchlevel mismatch: got '$out' want '$patchlevel'"} |
||||
} |
||||
log "PASS - built $patchlevel (checkout [string range $uuid 0 11]..., zig $zigv) -> $stage/out" |
||||
@ -0,0 +1,85 @@
|
||||
const std = @import("std"); |
||||
|
||||
const usage = |
||||
\\Usage: ./wrapfiletofile [options] |
||||
\\ |
||||
\\Options: |
||||
\\ -prefix STRING |
||||
\\ -prefixnl BOOLEAN |
||||
\\ -input INPUTFILENAME |
||||
\\ -outut OUTPUTFILENAME |
||||
; |
||||
|
||||
pub fn main() !void { |
||||
var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
||||
defer arena_state.deinit(); |
||||
const arena = arena_state.allocator(); |
||||
|
||||
const args = try std.process.argsAlloc(arena); |
||||
|
||||
var opt_input_file_path: ?[]const u8 = null; |
||||
var opt_output_file_path: ?[]const u8 = null; |
||||
var opt_prefix: ?[]const u8 = null; |
||||
var opt_prefixnl: bool = true; |
||||
|
||||
{ |
||||
var i: usize = 1; |
||||
while (i < args.len) : (i += 1) { |
||||
const arg = args[i]; |
||||
if (std.mem.eql(u8, "-h",arg) or std.mem.eql(u8, "-help",arg)) { |
||||
try std.io.getStdOut().writeAll(usage); |
||||
return std.process.cleanExit(); |
||||
} else if (std.mem.eql(u8,"-prefix",arg)) { |
||||
i += 1; |
||||
if (i > args.len) fatal("expected arg after '{s}'", .{arg}); |
||||
opt_prefix = args[i]; |
||||
} else if (std.mem.eql(u8,"-input",arg)) { |
||||
i += 1; |
||||
if (i > args.len) fatal("expected arg after '{s}'", .{arg}); |
||||
if (opt_input_file_path != null) fatal("duplicated {s} argument", .{arg}); |
||||
opt_input_file_path = args[i]; |
||||
} else if (std.mem.eql(u8,"-output",arg)) { |
||||
i += 1; |
||||
if (i > args.len) fatal("expected arg after '{s}'", .{arg}); |
||||
if (opt_output_file_path != null) fatal("duplicated {s} argument", .{arg}); |
||||
opt_output_file_path = args[i]; |
||||
} else if (std.mem.eql(u8,"-prefixnl",arg)) { |
||||
i+= 1; |
||||
if (i > args.len) fatal("expected arg after '{s}'", .{arg}); |
||||
//cast? review |
||||
if (std.mem.eql(u8,"1",args[i])) { |
||||
opt_prefixnl = true; |
||||
} else { |
||||
opt_prefixnl = false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
const input_file_path = opt_input_file_path orelse fatal("missing -input file", .{}); |
||||
const output_file_path = opt_output_file_path orelse fatal("missing -output file", .{}); |
||||
const prefix = opt_prefix orelse fatal("missing -prefix string",.{}); |
||||
var input_file = std.fs.cwd().openFile(input_file_path,.{ .mode = .read_only}) catch |err| { |
||||
fatal("Unable to open '{s}': {s}",. { input_file_path, @errorName(err) }); |
||||
}; |
||||
defer input_file.close(); |
||||
const input_data = try input_file.readToEndAlloc(arena, std.math.maxInt(usize)); |
||||
defer arena.free(input_data); |
||||
std.debug.print("inputfile - Read {d} octets.\n", .{input_data.len}); |
||||
|
||||
var output_file = std.fs.cwd().createFile(output_file_path, .{}) catch |err| { |
||||
fatal("Unable to open '{s}': {s}", .{ output_file_path, @errorName(err)}); |
||||
}; |
||||
defer output_file.close(); |
||||
_ = try output_file.write(prefix); |
||||
if (opt_prefixnl) { |
||||
_ = try output_file.write("\n"); |
||||
} |
||||
try output_file.writeAll(input_data); |
||||
return std.process.cleanExit(); |
||||
} |
||||
|
||||
fn fatal(comptime format: []const u8, args: anytype) noreturn { |
||||
std.debug.print(format,args); |
||||
std.process.exit(1); |
||||
} |
||||
@ -0,0 +1,55 @@
|
||||
#JMN 2024 |
||||
#A simple wrapper to call the 'zipfs mkimg' (tcl::zipfs::mkimg) command via commandline arguments. |
||||
#(avoiding piping commands to tclsh via stdin to make calling simpler, and to provide more specific error messages) |
||||
# |
||||
set script_title "[file tail [info nameofexecutable]] [file tail [info script]]" |
||||
set usage "$script_title -outfile <target_zipfile_path> -indir <folder_to_zip> ?-strip <path_prefix_to_strip>? ?-password <plaintext>? ?-infile <explicit_prepend_file>?" |
||||
set arglist $::argv |
||||
puts stderr "====> '$arglist'" |
||||
#error 42 |
||||
if {![llength $arglist]} { |
||||
puts stderr $usage |
||||
exit 1 |
||||
} |
||||
if {[llength $arglist] %2 != 0} { |
||||
puts stderr "$script_title error: expected an even number of option value pairs. Got [llength $arglist] commandline arguments. '$arglist'" |
||||
puts stderr $usage |
||||
exit 2 |
||||
} |
||||
set known_opts [list -outfile -indir -strip -password -infile] |
||||
dict for {k v} $arglist { |
||||
switch -exact -- $k { |
||||
-outfile - -indir - -strip - -password - -infile {} |
||||
default { |
||||
puts stderr "$script_title error: unrecognised option $k. Known_options $known_opts" |
||||
exit 3 |
||||
} |
||||
} |
||||
} |
||||
set defaults [dict create\ |
||||
-strip ""\ |
||||
-password ""\ |
||||
] |
||||
|
||||
set opts [dict merge $defaults $arglist] |
||||
if {![dict exists $opts -outfile]} { |
||||
puts stderr "$script_title error: Missing -outfile <zipfilename>" |
||||
exit 4 |
||||
} |
||||
if {![dict exists $opts -indir]} { |
||||
puts stderr "$script_title error: Missing -indir <folder>" |
||||
exit 5 |
||||
} |
||||
set callargs [list [file normalize [dict get $opts -outfile]] [file normalize [dict get $opts -indir]] [file normalize [dict get $opts -strip]] [dict get $opts -password]] |
||||
if {[dict exists $opts -infile]} { |
||||
lappend callargs [file normalize [dict get $opts -infile]] |
||||
} |
||||
puts stderr "$script_title call 'tcl::zipfs::mkimg $callargs'" |
||||
|
||||
if {[catch { |
||||
tcl::zipfs::mkimg {*}$callargs |
||||
} errM]} { |
||||
puts stderr "$script_title error: Failure during call 'tcl::zipfs::mkimg $callargs'\n $errM" |
||||
exit 6 |
||||
} |
||||
exit 0 |
||||
@ -0,0 +1,65 @@
|
||||
#JMN 2024 |
||||
#A simple wrapper to call the 'zipfs mkzip' (tcl::zipfs::mkzip) command via commandline arguments. |
||||
#(avoiding piping commands to tclsh via stdin to make calling simpler, and to provide more specific error messages) |
||||
# |
||||
set script_title "[file tail [info nameofexecutable]] [file tail [info script]]" |
||||
set usage "$script_title -outfile <target_zipfile_path> -indir <folder_to_zip> ?-strip <path_prefix_to_strip>? ?-password <plaintext>?" |
||||
set arglist $::argv |
||||
#puts stderr "====> '$arglist'" |
||||
#error 42 |
||||
if {![llength $arglist]} { |
||||
puts stderr $usage |
||||
exit 1 |
||||
} |
||||
if {[llength $arglist] %2 != 0} { |
||||
puts stderr "$script_title error: expected an even number of option value pairs. Got [llength $arglist] commandline arguments. '$arglist'" |
||||
puts stderr $usage |
||||
exit 2 |
||||
} |
||||
set known_opts [list -outfile -indir -strip -password] |
||||
dict for {k v} $arglist { |
||||
switch -exact -- $k { |
||||
-outfile - -indir - -strip - -password {} |
||||
default { |
||||
puts stderr "$script_title error: unrecognised option $k. Known_options $known_opts" |
||||
exit 3 |
||||
} |
||||
} |
||||
} |
||||
set defaults [dict create\ |
||||
] |
||||
|
||||
set opts [dict merge $defaults $arglist] |
||||
if {![dict exists $opts -outfile]} { |
||||
puts stderr "$script_title error: Missing -outfile <zipfilename>" |
||||
exit 4 |
||||
} |
||||
if {![dict exists $opts -indir]} { |
||||
puts stderr "$script_title error: Missing -indir <folder>" |
||||
exit 5 |
||||
} |
||||
set callargs [list [file normalize [dict get $opts -outfile]] [file normalize [dict get $opts -indir]]] |
||||
if {[dict exists $opts -strip]} { |
||||
set opt_strip [dict get $opts -strip] |
||||
#avoid extraneous underscore(empty path segment?) in zip archive on windows. |
||||
#review - test if trailing slash needed on other platforms |
||||
#if {"windows" eq "$::tcl_platform(platform)" && $opt_strip ne "" && [lindex $opt_strip end] ne "\\"} { |
||||
# append opt_strip \\ |
||||
#} |
||||
lappend callargs [file normalize $opt_strip] |
||||
} |
||||
if {[dict exists $opts -password]} { |
||||
if {![dict exists $opts -strip]} { |
||||
lappend callargs "" |
||||
} |
||||
lappend callargs [dict get $opts -password] |
||||
} |
||||
puts stderr "$script_title call 'tcl::zipfs::mkzip $callargs'" |
||||
|
||||
if {[catch { |
||||
tcl::zipfs::mkzip {*}$callargs |
||||
} errM]} { |
||||
puts stderr "$script_title error: Failure during call 'tcl::zipfs::mkzip $callargs'\n $errM" |
||||
exit 6 |
||||
} |
||||
exit 0 |
||||
Loading…
Reference in new issue