diff --git a/CHANGELOG.md b/CHANGELOG.md index aa28af52..c9c0b7a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. +## [0.17.9] - 2026-07-22 + +- `bin/punk-runtime.cmd` (G-103/G-105 groundwork): cross-platform staging surface - `fetch`/`list`/`use` accept `-platform
` (punkbin platform-dir names; default = local platform, overridable via `PUNK_RUNTIME_PLATFORM` env) for listing/fetching/selecting runtimes of OTHER platforms (cross-build staging and guest provisioning); foreign fetches require an explicit runtime name; `list` flags runtimes whose metadata `target` disagrees with their folder (`!TARGET-MISMATCH`); `run` stays local-only (rejects `-platform`, ignores the env override). New `help` action (full operator reference: actions, options, env vars, examples) and a proper usage block on no-args invocation. Bash payload's FreeBSD platform dir aligned to punkbin's actual `freebsd-x86_64` (was `freebsd-amd64`). Layout-shipped copy refreshed. + ## [0.17.8] - 2026-07-22 - punk::console 0.8.0 (G-106): the powershell console-mode fallback (raw mode on twapi-less windows runtimes) is now dependable and quiet. The persistent pwsh/powershell server starts lazily on first raw enable/disable (loading punk::console no longer spawns a process or prints "twapi not present..." to stderr), is shared process-wide, watches the owning process's pid and exits with it (no orphan pwsh processes), and no longer dies mid-session (the old 20s keepalive with no ping sender, plus a listener defect that killed it after the first message, are both fixed). disableRaw now works via the server on runtimes without `-inputmode` (tcl 8.6 path); enableRaw confirms the flip via the live tcl9 `-inputmode` read before returning. The server script resolves from env `PUNK_PS_CONSOLEMODE_SCRIPT`, the project tree, or an embedded copy in the module - kits and unusual cwds no longer depend on argv0-relative luck. `PUNK_PS_CONSOLEMODE_DEBUG=1` surfaces diagnostics. `scriptlib/utils/pwsh` reconciled to the one canonical server script (experiment variants retired, README added). diff --git a/bin/AGENTS.md b/bin/AGENTS.md index 9b570fc8..2040e746 100644 --- a/bin/AGENTS.md +++ b/bin/AGENTS.md @@ -99,6 +99,24 @@ projects reference), copies the metadata toml alongside, and selects the working (dotted tcl patchlevels make generic last-dot stripping wrong for extensionless unix names). Candidate listing excludes directories and `.txt/.toml/.tm/.tmp/.log` files. +Cross-platform surface (G-105 groundwork; both payloads): `fetch`/`list`/`use` accept +`-platform
` where `
` is a punkbin platform-DIR name (`win32-x86_64`, +`linux-x86_64`, `macosx`, ... - never zig triples; the buildsuite maps triples to +platform dirs at build time). Resolution: `-platform` arg > `PUNK_RUNTIME_PLATFORM` +env > the detected local platform; validation is shape-only, lowercase (the artifact +server's URL paths are case-sensitive - the server is the truth for what exists). +Foreign-platform folders serve cross-build staging/provisioning: a foreign `fetch` +requires an explicit runtime name (no foreign defaults), `use -platform` manages that +folder's `active.toml`/materialization (the marker travels with the folder when +deployed; unix exec bits are restored at deploy time), and `list` flags a runtime +whose metadata `target` disagrees with the folder it sits in (`!TARGET-MISMATCH`). +`run` is LOCAL ONLY - it rejects a leading `-platform` and ignores the env override +(later `run` args pass through to the runtime untouched). A no-args invocation prints +a short usage block; the `help` action gives the full operator reference (actions, +options, env vars incl `PUNKBIN_URL`, examples). The bash payload's FreeBSD platform +dir was aligned to punkbin's actual folder name (`freebsd-x86_64`; previously +`freebsd-amd64`). + ### Interactive verification shells - Interactive console/repl verification should cover both Tcl generations - behaviour can differ materially (e.g. the Tcl 8.6 windows console channel driver vs the Tcl 9 rewrite). Use a Tcl 8.6-based punk shell (`punksys.exe`) and a current Tcl 9-based punk shell (named for the Tcl release it embeds, e.g. `punk902z.exe` at the time of writing - ask the user which is current rather than assuming). `info patchlevel` in-session confirms the runtime. diff --git a/bin/punk-runtime.cmd b/bin/punk-runtime.cmd index 1f713340..88b619ed 100755 --- a/bin/punk-runtime.cmd +++ b/bin/punk-runtime.cmd @@ -1299,55 +1299,57 @@ arch=$(uname -m) #machine/architecture plat=$(uname -s) #platform/system #even though most of the platform prongs are very similar, #we keep the code separate so it can be tweaked easily for unexpected differences -#each prong sets archdir (local folder) and, where a runtime is published for the -#platform, rt_default (default runtime name). archtail (server folder) is derived -#from archdir below. +#each prong sets local_platform (the punkbin/bin-runtime platform-DIR name) and, +#where a runtime is published for the platform, rt_default (default runtime name). if [[ "$plat" = "Linux"* ]]; then if [[ "$arch" = "x86_64"* ]]; then - archdir="${scriptdir}/runtime/linux-x86_64" + local_platform="linux-x86_64" rt_default="tclkit-902-Linux64-intel-dyn" runtime_available=1 elif [[ "$arch" = "arm"* ]]; then - archdir="${scriptdir}/runtime/linux-arm" + local_platform="linux-arm" rt_default="tclkit-902-Linux64-arm-dyn" runtime_available=1 else - archdir="${scriptdir}/runtime/linux-$arch" + local_platform="linux-$arch" fi os="linux" elif [[ "$plat" = "Darwin"* ]]; then os="macosx" #assumed to be Mach-O 'universal binaries' for both x86-64 and arm? - REVIEW - archdir="${scriptdir}/runtime/macosx" + local_platform="macosx" rt_default="tclkit-902-Darwin64-dyn" runtime_available=1 elif [[ "$plat" = "FreeBSD"* ]]; then - archdir="${scriptdir}/runtime/freebsd-amd64" + #aligned 2026-07-22 to the punkbin repo's actual folder name (freebsd-x86_64; + #this payload previously said freebsd-amd64 - the G-105 platform-dir naming + #alignment item) + local_platform="freebsd-x86_64" os="freebsd" elif [[ "$plat" == "DragonFly"* ]]; then - archdir="${scriptdir}/runtime/dragonflybsd-$arch" + local_platform="dragonflybsd-$arch" os="dragonflybsd" elif [[ "$plat" == "NetBSD"* ]]; then - archdir="${scriptdir}/runtime/netbsd-$arch" + local_platform="netbsd-$arch" os="netbsd" elif [[ "$plat" == "OpenBSD"* ]]; then - archdir="${scriptdir}/runtime/openbsd-amd64" + local_platform="openbsd-amd64" os="openbsd" elif [[ "$plat" == "MINGW32"* ]]; then #REVIEW os="win32" - archdir="${scriptdir}/runtime/win32-x86_64" + local_platform="win32-x86_64" rt_default="tclsh902z.exe" runtime_available=1 elif [[ "$plat" == "MINGW64"* ]]; then #REVIEW os="win32" - archdir="${scriptdir}/runtime/win32-x86_64" + local_platform="win32-x86_64" rt_default="tclsh902z.exe" runtime_available=1 elif [[ "$plat" == "CYGWIN_NT"* ]]; then os="win32" - archdir="${scriptdir}/runtime/win32-x86_64" + local_platform="win32-x86_64" rt_default="tclsh902z.exe" runtime_available=1 elif [[ "$plat" == "MSYS_NT"* ]]; then @@ -1360,14 +1362,61 @@ elif [[ "$plat" == "MSYS_NT"* ]]; then #"c:/windows/system32/" is quite likely in the path ahead of msys,git etc. #This breaks calls to various unix utils such as sed etc (wsl related?) export PATH="$shellfolder${PATH:+:${PATH}}" - archdir="${scriptdir}/runtime/win32-x86_64" + local_platform="win32-x86_64" rt_default="tclsh902z.exe" runtime_available=1 else - archdir="${scriptdir}/runtime/other" + local_platform="other" os="other" fi -archtail="${archdir##*/}" #server folder name e.g linux-x86_64 + +#-- platform resolution (G-105 cross-build staging) --------------------------- +#The prongs above detect the LOCAL platform. fetch/list/use accept +#'-platform
' to aim at another punkbin platform-dir (cross-build staging /
+#provisioning); resolution: -platform arg > PUNK_RUNTIME_PLATFORM env > local.
+#Values are punkbin platform-dir names (e.g win32-x86_64, linux-x86_64, macosx)
+#- NOT zig triples (the buildsuite maps triples to platform dirs at build time).
+#Validation is shape-only: the server's sha1sums/404 is the truth for what
+#exists. 'run' is LOCAL ONLY (foreign binaries are not runnable here) - only a
+#LEADING -platform is rejected there; later args belong to the launched runtime.
+action="${1:-}"
+[[ $# -gt 0 ]] && shift
+platform_opt=""
+case "$action" in
+ fetch|list|use)
+ newargs=()
+ while [[ $# -gt 0 ]]; do
+ if [[ "$1" == "-platform" ]]; then
+ shift
+ if [[ $# -eq 0 ]]; then
+ echo "option -platform requires a value (a punkbin platform-dir name, e.g linux-x86_64)"
+ exit 1
+ fi
+ platform_opt="$1"
+ else
+ newargs+=("$1")
+ fi
+ shift
+ done
+ set -- ${newargs[@]+"${newargs[@]}"}
+ ;;
+ run)
+ if [[ "${1:-}" == "-platform" ]]; then
+ echo "'run' launches the LOCAL platform's active runtime - it takes no -platform option"
+ echo "(foreign-platform folders are cross-build staging; deploy them to their platform to run)"
+ exit 1
+ fi
+ ;;
+esac
+platform="${platform_opt:-${PUNK_RUNTIME_PLATFORM:-$local_platform}}"
+if ! [[ "$platform" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
+ echo "invalid platform name '$platform' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)"
+ exit 1
+fi
+is_local=0
+[[ "$platform" == "$local_platform" ]] && is_local=1
+archdir="${scriptdir}/runtime/${platform}"
+archtail="$platform" #server folder name e.g linux-x86_64
active_file="${archdir}/active.toml"
#sha1 of a file - tool varies by platform (sha1sum: linux/coreutils, shasum: macosx,
@@ -1425,7 +1474,7 @@ rootname_of() {
#by the buildsuite kit-family-artifacts step / fetched from punkbin). Prints a
#short "[variant=... tcl=... rN ...]" summary for list output, "" when absent.
metadata_summary() {
- local exename="$1" tomlfile parts variant tclpatch revision piperepl artname
+ local exename="$1" tomlfile parts variant tclpatch revision piperepl artname target
tomlfile="$archdir/$(rootname_of "$exename").toml"
[[ -f "$tomlfile" ]] || return 0
variant=$(sed -n 's/^[[:space:]]*variant[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
@@ -1433,6 +1482,7 @@ metadata_summary() {
revision=$(sed -n 's/^[[:space:]]*revision[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$tomlfile" | head -n 1)
piperepl=$(sed -n 's/^[[:space:]]*piperepl[[:space:]]*=[[:space:]]*\(true\|false\).*/\1/p' "$tomlfile" | head -n 1)
artname=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
+ target=$(sed -n 's/^[[:space:]]*target[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
parts=""
[[ -n "$variant" ]] && parts="$parts variant=$variant"
[[ -n "$tclpatch" ]] && parts="$parts tcl=$tclpatch"
@@ -1441,8 +1491,70 @@ metadata_summary() {
elif [[ "$piperepl" == "false" ]]; then parts="$parts piperepl=off"; fi
#a materialized working copy records which immutable artifact it came from
[[ -n "$artname" && "$artname" != "$exename" ]] && parts="$parts from=$artname"
+ #integrity flag: a runtime filed under a platform folder its metadata says it
+ #was not built for (cross-platform fetch/staging misfiling)
+ [[ -n "$target" && "$target" != "$archtail" ]] && parts="$parts !TARGET-MISMATCH:$target"
[[ -n "$parts" ]] && printf '[%s]' "${parts# }"
}
+#operator help (the 'help' action; the no-args case shows show_usage only).
+#Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage.
+show_usage() {
+ echo "Usage: $0 {fetch|list|use|run|help}"
+ echo " fetch ? ? download+verify a runtime from punkbin"
+ echo " list ?-remote? ?-platform ? installed (or server) runtimes + metadata"
+ echo " use ? select active / materialize -r ?"
+ echo " Download a runtime from the punkbin artifact server into"
+ echo " bin/runtime/ / with sha1 verification against the server's"
+ echo " sha1sums.txt. -r ?"
+ echo " List installed runtimes with artifact-metadata summaries"
+ echo " (variant, tcl patchlevel, revision, piperepl policy, source"
+ echo " artifact of a materialized copy; a !TARGET-MISMATCH flag marks"
+ echo " a runtime filed under the wrong platform folder). -remote"
+ echo " compares local runtimes against the server's sha1sums."
+ echo " use ?"
+ echo " Select the runtime that 'run' launches (records active.toml in"
+ echo " the platform folder). An immutable -r ?"
+ echo "Installed candidates ($platform):"
for f in $(list_candidates); do
echo " $f"
done
@@ -1632,14 +1754,14 @@ case "$1" in
fi
exit 1
fi
- case "$2" in
+ case "$1" in
*_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp)
- echo "'$2' is not a selectable runtime"
+ echo "'$1' is not a selectable runtime"
exit 1
;;
esac
- if [[ ! -f "$archdir/$2" ]]; then
- echo "No runtime named '$2' found in $archdir"
+ if [[ ! -f "$archdir/$1" ]]; then
+ echo "No runtime named '$1' found in $archdir"
echo "Installed candidates:"
for f in $(list_candidates); do
echo " $f"
@@ -1650,26 +1772,35 @@ case "$1" in
#artifact into its WORKING name (name minus -r ? download+verify a runtime from punkbin"
+ write-host " list ?-remote? ?-platform ? installed (or server) runtimes + metadata"
+ write-host " use ? select active / materialize -r ?"
+ write-host " Download a runtime from the punkbin artifact server into"
+ write-host " bin/runtime/ / with sha1 verification against the server's"
+ write-host " sha1sums.txt. -r ?"
+ write-host " List installed runtimes with artifact-metadata summaries"
+ write-host " (variant, tcl patchlevel, revision, piperepl policy, source"
+ write-host " artifact of a materialized copy; a !TARGET-MISMATCH flag marks"
+ write-host " a runtime filed under the wrong platform folder). -remote"
+ write-host " compares local runtimes against the server's sha1sums."
+ write-host " use ?"
+ write-host " Select the runtime that 'run' launches (records active.toml in"
+ write-host " the platform folder). An immutable -r ?"
+ write-host "Installed candidates ($arch):"
foreach ($f in $candidates) {
write-host " $($f.Name)"
}
@@ -2417,8 +2683,16 @@ function psmain {
'run' {
#launch the active runtime, passing arguments.
#resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single
- #installed candidate - otherwise error with candidates (no last-in-list guessing)
- $arch = "win32-x86_64"
+ #installed candidate - otherwise error with candidates (no last-in-list guessing).
+ #LOCAL PLATFORM ONLY - a foreign platform's binaries are not runnable here.
+ #Only a LEADING -platform is rejected: everything after 'run' belongs to
+ #the runtime, so a later arg spelled -platform must pass through untouched.
+ if ($PSBoundParameters.opts.Length -gt 0 -and $PSBoundParameters.opts[0] -eq '-platform') {
+ write-host "'run' launches the LOCAL platform's active runtime - it takes no -platform option"
+ write-host "(foreign-platform folders are cross-build staging; deploy them to their platform to run)"
+ exit 1
+ }
+ $arch = $script:PunkLocalPlatform
$archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
if (-not(Test-Path -Path $archfolder -PathType Container)) {
write-host "No runtimes seem to be installed for $arch`nPlease use 'punk-runtime.cmd fetch' to install"
@@ -2494,9 +2768,9 @@ function psmain {
}
}
'list' {
- #todo - option to list for other os-arch
- $arch = 'win32-x86_64'
- $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
+ #-platform lists another platform's folder/server dir (cross-build staging)
+ $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"]
+ $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
$sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt"
$archurl = "$artifacturl/$arch"
$sha1url = "$archurl/sha1sums.txt"
@@ -2595,7 +2869,7 @@ function psmain {
write-host "$(${dircontents}.count) runtime(s) in $archfolder"
Write-host "-----------------------------------------------------------------------"
foreach ($f in $dircontents) {
- $meta = Get-PunkRuntimeMetadataSummary $archfolder $f.Name
+ $meta = Get-PunkRuntimeMetadataSummary $archfolder $f.Name $arch
$lhs = "$($f.Name)".PadRight(35, ' ')
if ($f.Name -eq $activename) {
write-host "* $lhs (active) $meta"
@@ -2616,13 +2890,15 @@ function psmain {
}
}
}
+ 'help' {
+ Show-PunkRuntimeHelp
+ }
default {
- $actions = @("fetch", "list", "use", "run")
- write-host "Available actions: $actions"
- write-host "received"
- foreach ($boundparam in $PSBoundParameters.opts) {
- write-host $boundparam
- }
+ #no action given (unknown actions were already rejected in the
+ #process block): show the short usage and point at 'help'
+ Show-PunkRuntimeUsage
+ write-host ""
+ write-host "'punk-runtime.cmd help' gives options, env vars and examples."
}
}
diff --git a/goals/G-103-runtime-kit-family.md b/goals/G-103-runtime-kit-family.md
index 07f6a350..ef8c72ed 100644
--- a/goals/G-103-runtime-kit-family.md
+++ b/goals/G-103-runtime-kit-family.md
@@ -372,6 +372,28 @@ punk family runtime and reported 9.0.5 / piperepl machinery present /
tcl_library=//zipfs:/app/tcl_library. Project 0.17.7 (patch bump + changelog:
punk-runtime is shipped product surface).
+### 2026-07-22 increment: punk-runtime cross-platform surface + help (user-directed; G-105 groundwork)
+
+User-approved argument surface for future cross-builds (design discussion
+2026-07-22): fetch/list/use accept `-platform ` (punkbin platform-DIR names,
+never zig triples; resolution -platform arg > PUNK_RUNTIME_PLATFORM env > local
+default; shape-only lowercase validation - the server is the truth). Foreign
+fetch requires an explicit runtime name (no foreign defaults); `use -platform`
+manages that folder's active.toml/materialization (the marker travels with the
+folder at deploy time - the provisioning story; exec bits restored on the
+receiving side); `list` gains a !TARGET-MISMATCH integrity flag when a
+runtime's metadata target disagrees with its folder; `run` is local-only
+(rejects a leading -platform, ignores the env override, later args pass to the
+runtime untouched). Plus a `help` action (full operator reference) and a real
+usage block on no-args. Bash FreeBSD platform dir aligned to punkbin's actual
+freebsd-x86_64 (was freebsd-amd64 - a G-105 naming-alignment item found during
+the design pass). Parity bug caught in verification: powershell -notmatch is
+case-insensitive - validation uses -cnotmatch (URL paths are case-sensitive).
+Verified on both payloads directly AND through the rewrapped polyglot
+(noargs/help/list/foreign-use-materialize/mismatch-flag/foreign-fetch-refusal/
+run-guard/env-override incl run's immunity to it); roundtrip pin PASS; layout
+copy + .ps1 twin refreshed; project 0.17.9.
+
Remaining for acceptance:
- exercise the new mapvfs entries with an actual kit wrap (punk9_beta /
diff --git a/goals/G-105-buildsuite-cross-target.md b/goals/G-105-buildsuite-cross-target.md
index 204cdf0b..30078364 100644
--- a/goals/G-105-buildsuite-cross-target.md
+++ b/goals/G-105-buildsuite-cross-target.md
@@ -63,3 +63,13 @@ dimension), G-102 (achieved 2026-07-21 - driver shape settled; target becomes a
is fully characterized (confirmed flaw, both direct-kit and modpod-.tm binary
loads - G-103/G-066 Notes); the linux expectation stays no-accumulation
(unlink-while-mapped) but is UNVERIFIED until a runtime exists.
+- 2026-07-22: punk-runtime grew the cross-platform staging surface this goal
+ will consume (user-approved; details in G-103 Progress 2026-07-22):
+ fetch/list/use -platform ' to aim at another punkbin platform-dir (cross-build staging /
+#provisioning); resolution: -platform arg > PUNK_RUNTIME_PLATFORM env > local.
+#Values are punkbin platform-dir names (e.g win32-x86_64, linux-x86_64, macosx)
+#- NOT zig triples (the buildsuite maps triples to platform dirs at build time).
+#Validation is shape-only: the server's sha1sums/404 is the truth for what
+#exists. 'run' is LOCAL ONLY (foreign binaries are not runnable here) - only a
+#LEADING -platform is rejected there; later args belong to the launched runtime.
+action="${1:-}"
+[[ $# -gt 0 ]] && shift
+platform_opt=""
+case "$action" in
+ fetch|list|use)
+ newargs=()
+ while [[ $# -gt 0 ]]; do
+ if [[ "$1" == "-platform" ]]; then
+ shift
+ if [[ $# -eq 0 ]]; then
+ echo "option -platform requires a value (a punkbin platform-dir name, e.g linux-x86_64)"
+ exit 1
+ fi
+ platform_opt="$1"
+ else
+ newargs+=("$1")
+ fi
+ shift
+ done
+ set -- ${newargs[@]+"${newargs[@]}"}
+ ;;
+ run)
+ if [[ "${1:-}" == "-platform" ]]; then
+ echo "'run' launches the LOCAL platform's active runtime - it takes no -platform option"
+ echo "(foreign-platform folders are cross-build staging; deploy them to their platform to run)"
+ exit 1
+ fi
+ ;;
+esac
+platform="${platform_opt:-${PUNK_RUNTIME_PLATFORM:-$local_platform}}"
+if ! [[ "$platform" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
+ echo "invalid platform name '$platform' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)"
+ exit 1
+fi
+is_local=0
+[[ "$platform" == "$local_platform" ]] && is_local=1
+archdir="${scriptdir}/runtime/${platform}"
+archtail="$platform" #server folder name e.g linux-x86_64
active_file="${archdir}/active.toml"
#sha1 of a file - tool varies by platform (sha1sum: linux/coreutils, shasum: macosx,
@@ -1425,7 +1474,7 @@ rootname_of() {
#by the buildsuite kit-family-artifacts step / fetched from punkbin). Prints a
#short "[variant=... tcl=... rN ...]" summary for list output, "" when absent.
metadata_summary() {
- local exename="$1" tomlfile parts variant tclpatch revision piperepl artname
+ local exename="$1" tomlfile parts variant tclpatch revision piperepl artname target
tomlfile="$archdir/$(rootname_of "$exename").toml"
[[ -f "$tomlfile" ]] || return 0
variant=$(sed -n 's/^[[:space:]]*variant[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
@@ -1433,6 +1482,7 @@ metadata_summary() {
revision=$(sed -n 's/^[[:space:]]*revision[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$tomlfile" | head -n 1)
piperepl=$(sed -n 's/^[[:space:]]*piperepl[[:space:]]*=[[:space:]]*\(true\|false\).*/\1/p' "$tomlfile" | head -n 1)
artname=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
+ target=$(sed -n 's/^[[:space:]]*target[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
parts=""
[[ -n "$variant" ]] && parts="$parts variant=$variant"
[[ -n "$tclpatch" ]] && parts="$parts tcl=$tclpatch"
@@ -1441,8 +1491,70 @@ metadata_summary() {
elif [[ "$piperepl" == "false" ]]; then parts="$parts piperepl=off"; fi
#a materialized working copy records which immutable artifact it came from
[[ -n "$artname" && "$artname" != "$exename" ]] && parts="$parts from=$artname"
+ #integrity flag: a runtime filed under a platform folder its metadata says it
+ #was not built for (cross-platform fetch/staging misfiling)
+ [[ -n "$target" && "$target" != "$archtail" ]] && parts="$parts !TARGET-MISMATCH:$target"
[[ -n "$parts" ]] && printf '[%s]' "${parts# }"
}
+#operator help (the 'help' action; the no-args case shows show_usage only).
+#Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage.
+show_usage() {
+ echo "Usage: $0 {fetch|list|use|run|help}"
+ echo " fetch ? ? download+verify a runtime from punkbin"
+ echo " list ?-remote? ?-platform ? installed (or server) runtimes + metadata"
+ echo " use ? select active / materialize -r ?"
+ echo " Download a runtime from the punkbin artifact server into"
+ echo " bin/runtime/ / with sha1 verification against the server's"
+ echo " sha1sums.txt. -r ?"
+ echo " List installed runtimes with artifact-metadata summaries"
+ echo " (variant, tcl patchlevel, revision, piperepl policy, source"
+ echo " artifact of a materialized copy; a !TARGET-MISMATCH flag marks"
+ echo " a runtime filed under the wrong platform folder). -remote"
+ echo " compares local runtimes against the server's sha1sums."
+ echo " use ?"
+ echo " Select the runtime that 'run' launches (records active.toml in"
+ echo " the platform folder). An immutable -r ?"
+ echo "Installed candidates ($platform):"
for f in $(list_candidates); do
echo " $f"
done
@@ -1632,14 +1754,14 @@ case "$1" in
fi
exit 1
fi
- case "$2" in
+ case "$1" in
*_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp)
- echo "'$2' is not a selectable runtime"
+ echo "'$1' is not a selectable runtime"
exit 1
;;
esac
- if [[ ! -f "$archdir/$2" ]]; then
- echo "No runtime named '$2' found in $archdir"
+ if [[ ! -f "$archdir/$1" ]]; then
+ echo "No runtime named '$1' found in $archdir"
echo "Installed candidates:"
for f in $(list_candidates); do
echo " $f"
@@ -1650,26 +1772,35 @@ case "$1" in
#artifact into its WORKING name (name minus -r ? download+verify a runtime from punkbin"
+ write-host " list ?-remote? ?-platform ? installed (or server) runtimes + metadata"
+ write-host " use ? select active / materialize -r ?"
+ write-host " Download a runtime from the punkbin artifact server into"
+ write-host " bin/runtime/ / with sha1 verification against the server's"
+ write-host " sha1sums.txt. -r ?"
+ write-host " List installed runtimes with artifact-metadata summaries"
+ write-host " (variant, tcl patchlevel, revision, piperepl policy, source"
+ write-host " artifact of a materialized copy; a !TARGET-MISMATCH flag marks"
+ write-host " a runtime filed under the wrong platform folder). -remote"
+ write-host " compares local runtimes against the server's sha1sums."
+ write-host " use ?"
+ write-host " Select the runtime that 'run' launches (records active.toml in"
+ write-host " the platform folder). An immutable -r ?"
+ write-host "Installed candidates ($arch):"
foreach ($f in $candidates) {
write-host " $($f.Name)"
}
@@ -2417,8 +2683,16 @@ function psmain {
'run' {
#launch the active runtime, passing arguments.
#resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single
- #installed candidate - otherwise error with candidates (no last-in-list guessing)
- $arch = "win32-x86_64"
+ #installed candidate - otherwise error with candidates (no last-in-list guessing).
+ #LOCAL PLATFORM ONLY - a foreign platform's binaries are not runnable here.
+ #Only a LEADING -platform is rejected: everything after 'run' belongs to
+ #the runtime, so a later arg spelled -platform must pass through untouched.
+ if ($PSBoundParameters.opts.Length -gt 0 -and $PSBoundParameters.opts[0] -eq '-platform') {
+ write-host "'run' launches the LOCAL platform's active runtime - it takes no -platform option"
+ write-host "(foreign-platform folders are cross-build staging; deploy them to their platform to run)"
+ exit 1
+ }
+ $arch = $script:PunkLocalPlatform
$archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
if (-not(Test-Path -Path $archfolder -PathType Container)) {
write-host "No runtimes seem to be installed for $arch`nPlease use 'punk-runtime.cmd fetch' to install"
@@ -2494,9 +2768,9 @@ function psmain {
}
}
'list' {
- #todo - option to list for other os-arch
- $arch = 'win32-x86_64'
- $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
+ #-platform lists another platform's folder/server dir (cross-build staging)
+ $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"]
+ $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
$sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt"
$archurl = "$artifacturl/$arch"
$sha1url = "$archurl/sha1sums.txt"
@@ -2595,7 +2869,7 @@ function psmain {
write-host "$(${dircontents}.count) runtime(s) in $archfolder"
Write-host "-----------------------------------------------------------------------"
foreach ($f in $dircontents) {
- $meta = Get-PunkRuntimeMetadataSummary $archfolder $f.Name
+ $meta = Get-PunkRuntimeMetadataSummary $archfolder $f.Name $arch
$lhs = "$($f.Name)".PadRight(35, ' ')
if ($f.Name -eq $activename) {
write-host "* $lhs (active) $meta"
@@ -2616,13 +2890,15 @@ function psmain {
}
}
}
+ 'help' {
+ Show-PunkRuntimeHelp
+ }
default {
- $actions = @("fetch", "list", "use", "run")
- write-host "Available actions: $actions"
- write-host "received"
- foreach ($boundparam in $PSBoundParameters.opts) {
- write-host $boundparam
- }
+ #no action given (unknown actions were already rejected in the
+ #process block): show the short usage and point at 'help'
+ Show-PunkRuntimeUsage
+ write-host ""
+ write-host "'punk-runtime.cmd help' gives options, env vars and examples."
}
}
diff --git a/src/scriptapps/bin/punk-runtime.bash b/src/scriptapps/bin/punk-runtime.bash
index a5a1d3f0..f10cbd20 100644
--- a/src/scriptapps/bin/punk-runtime.bash
+++ b/src/scriptapps/bin/punk-runtime.bash
@@ -19,55 +19,57 @@ arch=$(uname -m) #machine/architecture
plat=$(uname -s) #platform/system
#even though most of the platform prongs are very similar,
#we keep the code separate so it can be tweaked easily for unexpected differences
-#each prong sets archdir (local folder) and, where a runtime is published for the
-#platform, rt_default (default runtime name). archtail (server folder) is derived
-#from archdir below.
+#each prong sets local_platform (the punkbin/bin-runtime platform-DIR name) and,
+#where a runtime is published for the platform, rt_default (default runtime name).
if [[ "$plat" = "Linux"* ]]; then
if [[ "$arch" = "x86_64"* ]]; then
- archdir="${scriptdir}/runtime/linux-x86_64"
+ local_platform="linux-x86_64"
rt_default="tclkit-902-Linux64-intel-dyn"
runtime_available=1
elif [[ "$arch" = "arm"* ]]; then
- archdir="${scriptdir}/runtime/linux-arm"
+ local_platform="linux-arm"
rt_default="tclkit-902-Linux64-arm-dyn"
runtime_available=1
else
- archdir="${scriptdir}/runtime/linux-$arch"
+ local_platform="linux-$arch"
fi
os="linux"
elif [[ "$plat" = "Darwin"* ]]; then
os="macosx"
#assumed to be Mach-O 'universal binaries' for both x86-64 and arm? - REVIEW
- archdir="${scriptdir}/runtime/macosx"
+ local_platform="macosx"
rt_default="tclkit-902-Darwin64-dyn"
runtime_available=1
elif [[ "$plat" = "FreeBSD"* ]]; then
- archdir="${scriptdir}/runtime/freebsd-amd64"
+ #aligned 2026-07-22 to the punkbin repo's actual folder name (freebsd-x86_64;
+ #this payload previously said freebsd-amd64 - the G-105 platform-dir naming
+ #alignment item)
+ local_platform="freebsd-x86_64"
os="freebsd"
elif [[ "$plat" == "DragonFly"* ]]; then
- archdir="${scriptdir}/runtime/dragonflybsd-$arch"
+ local_platform="dragonflybsd-$arch"
os="dragonflybsd"
elif [[ "$plat" == "NetBSD"* ]]; then
- archdir="${scriptdir}/runtime/netbsd-$arch"
+ local_platform="netbsd-$arch"
os="netbsd"
elif [[ "$plat" == "OpenBSD"* ]]; then
- archdir="${scriptdir}/runtime/openbsd-amd64"
+ local_platform="openbsd-amd64"
os="openbsd"
elif [[ "$plat" == "MINGW32"* ]]; then
#REVIEW
os="win32"
- archdir="${scriptdir}/runtime/win32-x86_64"
+ local_platform="win32-x86_64"
rt_default="tclsh902z.exe"
runtime_available=1
elif [[ "$plat" == "MINGW64"* ]]; then
#REVIEW
os="win32"
- archdir="${scriptdir}/runtime/win32-x86_64"
+ local_platform="win32-x86_64"
rt_default="tclsh902z.exe"
runtime_available=1
elif [[ "$plat" == "CYGWIN_NT"* ]]; then
os="win32"
- archdir="${scriptdir}/runtime/win32-x86_64"
+ local_platform="win32-x86_64"
rt_default="tclsh902z.exe"
runtime_available=1
elif [[ "$plat" == "MSYS_NT"* ]]; then
@@ -80,14 +82,61 @@ elif [[ "$plat" == "MSYS_NT"* ]]; then
#"c:/windows/system32/" is quite likely in the path ahead of msys,git etc.
#This breaks calls to various unix utils such as sed etc (wsl related?)
export PATH="$shellfolder${PATH:+:${PATH}}"
- archdir="${scriptdir}/runtime/win32-x86_64"
+ local_platform="win32-x86_64"
rt_default="tclsh902z.exe"
runtime_available=1
else
- archdir="${scriptdir}/runtime/other"
+ local_platform="other"
os="other"
fi
-archtail="${archdir##*/}" #server folder name e.g linux-x86_64
+
+#-- platform resolution (G-105 cross-build staging) ---------------------------
+#The prongs above detect the LOCAL platform. fetch/list/use accept
+#'-platform ' to aim at another punkbin platform-dir (cross-build staging /
+#provisioning); resolution: -platform arg > PUNK_RUNTIME_PLATFORM env > local.
+#Values are punkbin platform-dir names (e.g win32-x86_64, linux-x86_64, macosx)
+#- NOT zig triples (the buildsuite maps triples to platform dirs at build time).
+#Validation is shape-only: the server's sha1sums/404 is the truth for what
+#exists. 'run' is LOCAL ONLY (foreign binaries are not runnable here) - only a
+#LEADING -platform is rejected there; later args belong to the launched runtime.
+action="${1:-}"
+[[ $# -gt 0 ]] && shift
+platform_opt=""
+case "$action" in
+ fetch|list|use)
+ newargs=()
+ while [[ $# -gt 0 ]]; do
+ if [[ "$1" == "-platform" ]]; then
+ shift
+ if [[ $# -eq 0 ]]; then
+ echo "option -platform requires a value (a punkbin platform-dir name, e.g linux-x86_64)"
+ exit 1
+ fi
+ platform_opt="$1"
+ else
+ newargs+=("$1")
+ fi
+ shift
+ done
+ set -- ${newargs[@]+"${newargs[@]}"}
+ ;;
+ run)
+ if [[ "${1:-}" == "-platform" ]]; then
+ echo "'run' launches the LOCAL platform's active runtime - it takes no -platform option"
+ echo "(foreign-platform folders are cross-build staging; deploy them to their platform to run)"
+ exit 1
+ fi
+ ;;
+esac
+platform="${platform_opt:-${PUNK_RUNTIME_PLATFORM:-$local_platform}}"
+if ! [[ "$platform" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
+ echo "invalid platform name '$platform' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)"
+ exit 1
+fi
+is_local=0
+[[ "$platform" == "$local_platform" ]] && is_local=1
+archdir="${scriptdir}/runtime/${platform}"
+archtail="$platform" #server folder name e.g linux-x86_64
active_file="${archdir}/active.toml"
#sha1 of a file - tool varies by platform (sha1sum: linux/coreutils, shasum: macosx,
@@ -145,7 +194,7 @@ rootname_of() {
#by the buildsuite kit-family-artifacts step / fetched from punkbin). Prints a
#short "[variant=... tcl=... rN ...]" summary for list output, "" when absent.
metadata_summary() {
- local exename="$1" tomlfile parts variant tclpatch revision piperepl artname
+ local exename="$1" tomlfile parts variant tclpatch revision piperepl artname target
tomlfile="$archdir/$(rootname_of "$exename").toml"
[[ -f "$tomlfile" ]] || return 0
variant=$(sed -n 's/^[[:space:]]*variant[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
@@ -153,6 +202,7 @@ metadata_summary() {
revision=$(sed -n 's/^[[:space:]]*revision[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$tomlfile" | head -n 1)
piperepl=$(sed -n 's/^[[:space:]]*piperepl[[:space:]]*=[[:space:]]*\(true\|false\).*/\1/p' "$tomlfile" | head -n 1)
artname=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
+ target=$(sed -n 's/^[[:space:]]*target[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
parts=""
[[ -n "$variant" ]] && parts="$parts variant=$variant"
[[ -n "$tclpatch" ]] && parts="$parts tcl=$tclpatch"
@@ -161,8 +211,70 @@ metadata_summary() {
elif [[ "$piperepl" == "false" ]]; then parts="$parts piperepl=off"; fi
#a materialized working copy records which immutable artifact it came from
[[ -n "$artname" && "$artname" != "$exename" ]] && parts="$parts from=$artname"
+ #integrity flag: a runtime filed under a platform folder its metadata says it
+ #was not built for (cross-platform fetch/staging misfiling)
+ [[ -n "$target" && "$target" != "$archtail" ]] && parts="$parts !TARGET-MISMATCH:$target"
[[ -n "$parts" ]] && printf '[%s]' "${parts# }"
}
+#operator help (the 'help' action; the no-args case shows show_usage only).
+#Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage.
+show_usage() {
+ echo "Usage: $0 {fetch|list|use|run|help}"
+ echo " fetch ? ? download+verify a runtime from punkbin"
+ echo " list ?-remote? ?-platform ? installed (or server) runtimes + metadata"
+ echo " use ? select active / materialize -r ?"
+ echo " Download a runtime from the punkbin artifact server into"
+ echo " bin/runtime/ / with sha1 verification against the server's"
+ echo " sha1sums.txt. -r ?"
+ echo " List installed runtimes with artifact-metadata summaries"
+ echo " (variant, tcl patchlevel, revision, piperepl policy, source"
+ echo " artifact of a materialized copy; a !TARGET-MISMATCH flag marks"
+ echo " a runtime filed under the wrong platform folder). -remote"
+ echo " compares local runtimes against the server's sha1sums."
+ echo " use ?"
+ echo " Select the runtime that 'run' launches (records active.toml in"
+ echo " the platform folder). An immutable -r ?"
+ echo "Installed candidates ($platform):"
for f in $(list_candidates); do
echo " $f"
done
@@ -352,14 +474,14 @@ case "$1" in
fi
exit 1
fi
- case "$2" in
+ case "$1" in
*_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp)
- echo "'$2' is not a selectable runtime"
+ echo "'$1' is not a selectable runtime"
exit 1
;;
esac
- if [[ ! -f "$archdir/$2" ]]; then
- echo "No runtime named '$2' found in $archdir"
+ if [[ ! -f "$archdir/$1" ]]; then
+ echo "No runtime named '$1' found in $archdir"
echo "Installed candidates:"
for f in $(list_candidates); do
echo " $f"
@@ -370,26 +492,35 @@ case "$1" in
#artifact into its WORKING name (name minus -r ? download+verify a runtime from punkbin"
+ write-host " list ?-remote? ?-platform ? installed (or server) runtimes + metadata"
+ write-host " use ? select active / materialize -r ?"
+ write-host " Download a runtime from the punkbin artifact server into"
+ write-host " bin/runtime/ / with sha1 verification against the server's"
+ write-host " sha1sums.txt. -r ?"
+ write-host " List installed runtimes with artifact-metadata summaries"
+ write-host " (variant, tcl patchlevel, revision, piperepl policy, source"
+ write-host " artifact of a materialized copy; a !TARGET-MISMATCH flag marks"
+ write-host " a runtime filed under the wrong platform folder). -remote"
+ write-host " compares local runtimes against the server's sha1sums."
+ write-host " use ?"
+ write-host " Select the runtime that 'run' launches (records active.toml in"
+ write-host " the platform folder). An immutable -r ?"
+ write-host "Installed candidates ($arch):"
foreach ($f in $candidates) {
write-host " $($f.Name)"
}
@@ -402,8 +528,16 @@ function psmain {
'run' {
#launch the active runtime, passing arguments.
#resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single
- #installed candidate - otherwise error with candidates (no last-in-list guessing)
- $arch = "win32-x86_64"
+ #installed candidate - otherwise error with candidates (no last-in-list guessing).
+ #LOCAL PLATFORM ONLY - a foreign platform's binaries are not runnable here.
+ #Only a LEADING -platform is rejected: everything after 'run' belongs to
+ #the runtime, so a later arg spelled -platform must pass through untouched.
+ if ($PSBoundParameters.opts.Length -gt 0 -and $PSBoundParameters.opts[0] -eq '-platform') {
+ write-host "'run' launches the LOCAL platform's active runtime - it takes no -platform option"
+ write-host "(foreign-platform folders are cross-build staging; deploy them to their platform to run)"
+ exit 1
+ }
+ $arch = $script:PunkLocalPlatform
$archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
if (-not(Test-Path -Path $archfolder -PathType Container)) {
write-host "No runtimes seem to be installed for $arch`nPlease use 'punk-runtime.cmd fetch' to install"
@@ -479,9 +613,9 @@ function psmain {
}
}
'list' {
- #todo - option to list for other os-arch
- $arch = 'win32-x86_64'
- $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
+ #-platform lists another platform's folder/server dir (cross-build staging)
+ $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"]
+ $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
$sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt"
$archurl = "$artifacturl/$arch"
$sha1url = "$archurl/sha1sums.txt"
@@ -580,7 +714,7 @@ function psmain {
write-host "$(${dircontents}.count) runtime(s) in $archfolder"
Write-host "-----------------------------------------------------------------------"
foreach ($f in $dircontents) {
- $meta = Get-PunkRuntimeMetadataSummary $archfolder $f.Name
+ $meta = Get-PunkRuntimeMetadataSummary $archfolder $f.Name $arch
$lhs = "$($f.Name)".PadRight(35, ' ')
if ($f.Name -eq $activename) {
write-host "* $lhs (active) $meta"
@@ -601,13 +735,15 @@ function psmain {
}
}
}
+ 'help' {
+ Show-PunkRuntimeHelp
+ }
default {
- $actions = @("fetch", "list", "use", "run")
- write-host "Available actions: $actions"
- write-host "received"
- foreach ($boundparam in $PSBoundParameters.opts) {
- write-host $boundparam
- }
+ #no action given (unknown actions were already rejected in the
+ #process block): show the short usage and point at 'help'
+ Show-PunkRuntimeUsage
+ write-host ""
+ write-host "'punk-runtime.cmd help' gives options, env vars and examples."
}
}