From d3bfc4484cc2a306879c027fd1d29fae65973154 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Wed, 22 Jul 2026 10:10:10 +1000 Subject: [PATCH] G-103/G-105 groundwork: punk-runtime -platform surface, help action, no-args usage Cross-build staging surface (both payloads in parity, rewrapped polyglot, layout copy + .ps1 twin refreshed, roundtrip pin PASS): - fetch/list/use accept -platform

(punkbin platform-DIR names, never zig triples); resolution: -platform arg > PUNK_RUNTIME_PLATFORM env > detected local platform; shape-only lowercase validation (server URL paths are case-sensitive - ps1 uses -cnotmatch; default -notmatch is case-insensitive and passed Win32-X86_64-class typos in testing). - foreign fetch requires an explicit runtime name (fetch defaults only make sense for the platform you stand on). - use -platform manages that folder's active.toml/materialization - the marker travels with the folder when deployed (provisioning story; unix exec bits restored at deploy time). - list flags !TARGET-MISMATCH when a runtime's metadata target disagrees with the folder it sits in (cross-fetch misfiling guard). - run stays LOCAL ONLY: rejects a leading -platform, ignores the env override, later args pass through to the runtime untouched. - new 'help' action (actions, options, env vars, examples) and a real usage block on no-args invocation (was a bare action list). - bash FreeBSD platform dir aligned to punkbin's actual freebsd-x86_64 (was freebsd-amd64; the G-105 platform-dir naming-alignment item). Verified on both payloads directly and through the wrapped cmd: noargs, help, local list unchanged, foreign use-materialization + mismatch flags, foreign-fetch refusal, run guard, env override (and run's immunity to it), bad-name validation. Project 0.17.9. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- CHANGELOG.md | 4 + bin/AGENTS.md | 18 + bin/punk-runtime.cmd | 400 +++++++++++++++--- goals/G-103-runtime-kit-family.md | 22 + goals/G-105-buildsuite-cross-target.md | 10 + punkproject.toml | 2 +- .../punk/project-0.1/bin/punk-runtime.cmd | 400 +++++++++++++++--- src/scriptapps/bin/punk-runtime.bash | 222 ++++++++-- src/scriptapps/bin/punk-runtime.ps1 | 178 +++++++- 9 files changed, 1069 insertions(+), 187 deletions(-) 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 ?? ?-platform

? download+verify a runtime from punkbin" + echo " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" + echo " use ?-platform

? select active / materialize -r artifact" + echo " run ?args...? launch the active local-platform runtime" + echo " help full help (options, env vars, examples)" +} +show_help() { + echo "punk-runtime - manage the plain Tcl runtimes under bin/runtime//" + echo "" + show_usage + echo "" + echo "Actions:" + echo " fetch ?? ?-platform

?" + echo " Download a runtime from the punkbin artifact server into" + echo " bin/runtime/

/ with sha1 verification against the server's" + echo " sha1sums.txt. -r-named family artifacts also fetch their" + echo " .toml metadata record. Omitting fetches the" + echo " local platform's default runtime; a FOREIGN platform fetch" + echo " requires an explicit ." + echo " list ?-remote? ?-platform

?" + 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 ?-platform

?" + echo " Select the runtime that 'run' launches (records active.toml in" + echo " the platform folder). An immutable -r ARTIFACT name is" + echo " MATERIALIZED into its WORKING name (name minus -r - what" + echo " mapvfs and projects reference), metadata toml copied alongside." + echo " run ?args...?" + echo " Launch the ACTIVE runtime of the LOCAL platform, passing args." + echo " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole" + echo " installed candidate. Takes no -platform (foreign binaries are" + echo " not runnable here)." + echo "" + echo "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64," + echo " macosx) - not zig triples. Default is the local platform; override" + echo " order: -platform argument > PUNK_RUNTIME_PLATFORM env var." + echo " Foreign-platform folders serve cross-build staging and provisioning:" + echo " active.toml travels with the folder when it is deployed to its real" + echo " platform (unix exec bits are restored at deploy time - e.g rsync/tar/" + echo " chmod on the receiving side)." + echo "" + echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," + echo " PUNK_ACTIVE_RUNTIME (run-time selection override)" + echo "" + echo "Examples:" + echo " $0 fetch tclsh9.0.5-punk-r1.exe" + echo " $0 use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)" + echo " $0 list -remote -platform linux-x86_64" + echo " $0 fetch tclsh9.0.5-punk-r1 -platform linux-x86_64" + echo " $0 use tclsh9.0.5-punk-r1 -platform linux-x86_64" + echo " $0 run myscript.tcl arg1 arg2" +} #G-103 artifact-tier names carry -r before the (optional) .exe suffix; prints #the WORKING name (artifact minus -r) when the name is artifact-tier, else "" working_name_of() { @@ -1451,13 +1563,20 @@ working_name_of() { if [[ "$w" != "$1" ]]; then printf '%s' "$w"; fi } -case "$1" in +case "$action" in "fetch") runtime="$rt_default" - if [[ -n "$2" ]]; then - runtime="$2" + if [[ -n "${1:-}" ]]; then + runtime="$1" + elif [[ "$is_local" -ne 1 ]]; then + #fetch-name defaults only make sense for the platform we are standing + #on - a foreign-platform fetch (cross-build staging) names its runtime + echo "No default runtime for foreign platform '$platform' - name it explicitly:" + echo " $0 fetch -platform $platform" + echo " (see available names: $0 list -remote -platform $platform)" + exit 1 fi - if [[ ( "$runtime_available" -eq 1 || -n "$2" ) && "$archtail" != "other" && -n "$runtime" ]]; then + if [[ ( "$runtime_available" -eq 1 || -n "${1:-}" ) && "$archtail" != "other" && -n "$runtime" ]]; then url="${url_kitbase}/${archtail}/${runtime}" output="${archdir}/${runtime}" sha1url="${url_kitbase}/${archtail}/sha1sums.txt" @@ -1547,7 +1666,7 @@ case "$1" in fi ;; "list") - if [[ "$2" == "-remote" ]]; then + if [[ "${1:-}" == "-remote" ]]; then #compare local runtimes with those listed on the artifact server #(functional parity with the powershell payload's 'list -remote') sha1url="${url_kitbase}/${archtail}/sha1sums.txt" @@ -1620,9 +1739,12 @@ case "$1" in fi ;; "use") - if [[ -z "$2" ]]; then - echo "Usage: $0 use " - echo "Installed candidates:" + #with -platform: manage THAT platform folder's selection/materialization + #(cross-build staging - active.toml travels with the folder when deployed; + #'run' on this machine only ever consults the local platform) + if [[ -z "${1:-}" ]]; then + echo "Usage: $0 use ?-platform

?" + 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 - what mapvfs and #projects reference), copies its metadata toml alongside, and selects #the working name. Republishing artifacts never churns consumers. - working=$(working_name_of "$2") + #(chmod is a no-op when staging unix runtimes from windows filesystems - + #exec bits are restored at deploy time, e.g rsync/tar/chmod on the guest.) + working=$(working_name_of "$1") if [[ -n "$working" ]]; then - cp -f "$archdir/$2" "$archdir/$working" + cp -f "$archdir/$1" "$archdir/$working" chmod +x "$archdir/$working" - srctoml="$archdir/$(rootname_of "$2").toml" + srctoml="$archdir/$(rootname_of "$1").toml" desttoml="$archdir/$(rootname_of "$working").toml" if [[ -f "$srctoml" ]]; then cp -f "$srctoml" "$desttoml" - echo "materialized $working from artifact $2 (metadata toml copied alongside)" + echo "materialized $working from artifact $1 (metadata toml copied alongside)" else - echo "materialized $working from artifact $2 (no metadata toml found beside the artifact)" + echo "materialized $working from artifact $1 (no metadata toml found beside the artifact)" fi set_active "$working" else - set_active "$2" + set_active "$1" fi ;; "run") #resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single - #installed candidate - otherwise error with candidates (no last-in-list guessing) + #installed candidate - otherwise error with candidates (no last-in-list guessing). + #LOCAL platform only: the -platform arg is rejected in the scan above, and a + #PUNK_RUNTIME_PLATFORM env override is likewise ignored here (foreign + #binaries are not runnable; the env override serves list/fetch/use). + if [[ "$is_local" -ne 1 ]]; then + archdir="${scriptdir}/runtime/${local_platform}" + active_file="${archdir}/active.toml" + fi activeruntime="" if [[ -n "$PUNK_ACTIVE_RUNTIME" ]]; then activeruntime="$PUNK_ACTIVE_RUNTIME" @@ -1706,13 +1837,22 @@ case "$1" in fi activeruntime_fullpath="$archdir/$activeruntime" #echo "using $activeruntime_fullpath" - shift - #echo "args: $@" + #(the action was already shifted off during the option scan - "$@" is + #exactly the runtime's argument list) $activeruntime_fullpath "$@" ;; + "help") + show_help + ;; + "") + #no action: short usage + pointer at the fuller help + show_usage + echo "" + echo "'$0 help' gives options, env vars and examples." + ;; *) - echo "Usage: $0 {fetch|list|use|run}" - echo "received $@" + echo "unknown action '$action'" + show_usage exit 1 ;; esac @@ -2085,6 +2225,91 @@ function Get-PunkRuntimeCandidates { } return @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not ($(".txt",".toml",".tm",".tmp",".log") -contains $_.Extension) } | Sort-Object Name) } +#operator help (the 'help' action; the no-args case shows the Usage block only). +#Keep in sync with the bash payload's show_help/show_usage. +function Show-PunkRuntimeUsage { + write-host "Usage: punk-runtime.cmd {fetch|list|use|run|help}" + write-host " fetch ?? ?-platform

? download+verify a runtime from punkbin" + write-host " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" + write-host " use ?-platform

? select active / materialize -r artifact" + write-host " run ?args...? launch the active local-platform runtime" + write-host " help full help (options, env vars, examples)" +} +function Show-PunkRuntimeHelp { + write-host "punk-runtime - manage the plain Tcl runtimes under bin/runtime//" + write-host "" + Show-PunkRuntimeUsage + write-host "" + write-host "Actions:" + write-host " fetch ?? ?-platform

?" + 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-named family artifacts also fetch their" + write-host " .toml metadata record. Omitting fetches the" + write-host " local platform's default runtime; a FOREIGN platform fetch" + write-host " requires an explicit ." + write-host " list ?-remote? ?-platform

?" + 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 ?-platform

?" + write-host " Select the runtime that 'run' launches (records active.toml in" + write-host " the platform folder). An immutable -r ARTIFACT name is" + write-host " MATERIALIZED into its WORKING name (name minus -r - what" + write-host " mapvfs and projects reference), metadata toml copied alongside." + write-host " run ?args...?" + write-host " Launch the ACTIVE runtime of the LOCAL platform, passing args." + write-host " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole" + write-host " installed candidate. Takes no -platform (foreign binaries are" + write-host " not runnable here)." + write-host "" + write-host "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64," + write-host " macosx) - not zig triples. Default is the local platform; override" + write-host " order: -platform argument > PUNK_RUNTIME_PLATFORM env var." + write-host " Foreign-platform folders serve cross-build staging and provisioning:" + write-host " active.toml travels with the folder when it is deployed to its real" + write-host " platform (unix exec bits are restored at deploy time - e.g rsync/tar/" + write-host " chmod on the receiving side)." + write-host "" + write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," + write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)" + write-host "" + write-host "Examples:" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1.exe" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)" + write-host " punk-runtime.cmd list -remote -platform linux-x86_64" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd run myscript.tcl arg1 arg2" +} +#platform (target) resolution for fetch/list/use: -platform arg > PUNK_RUNTIME_PLATFORM +#env > the local default. Values are punkbin/bin-runtime 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; punk-runtime speaks only the punkbin tier). Validation is +#deliberately shape-only: the server's sha1sums/404 is the truth for what exists. +#This payload runs on windows - the local default stays win32-x86_64 (a windows-arm +#default becomes a question when punkbin carries such a folder). +$script:PunkLocalPlatform = "win32-x86_64" +function Resolve-PunkRuntimePlatform { + param([string] $requested) + $plat = $script:PunkLocalPlatform + if ($requested) { + $plat = $requested + } elseif ($env:PUNK_RUNTIME_PLATFORM) { + $plat = $env:PUNK_RUNTIME_PLATFORM + } + #-cnotmatch: platform-dir names are lowercase and the artifact-server URL path + #is case-sensitive (default -notmatch is case-insensitive and would pass typos + #like Win32-X86_64 through to a server 404); bash payload parity (=~ is + #case-sensitive there) + if ($plat -cnotmatch '^[a-z0-9][a-z0-9_-]*$') { + write-host "invalid platform name '$plat' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)" + exit 1 + } + return $plat +} #name minus a .exe suffix only - dotted tcl patchlevels (tclsh9.0.5-punk) make #generic last-dot extension stripping wrong for extensionless unix names function Get-PunkRuntimeRootName { @@ -2098,7 +2323,7 @@ function Get-PunkRuntimeRootName { #by the buildsuite kit-family-artifacts step / fetched from punkbin). Returns a #short "[variant=... tcl=... rN ...]" summary for list output, "" when absent. function Get-PunkRuntimeMetadataSummary { - param([string] $archfolder, [string] $exename) + param([string] $archfolder, [string] $exename, [string] $expectedplatform = "") $tomlfile = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $exename) + ".toml") if (-not (Test-Path -Path $tomlfile -PathType Leaf)) { return "" @@ -2124,6 +2349,11 @@ function Get-PunkRuntimeMetadataSummary { } #a materialized working copy records which immutable artifact it came from if ($fields.ContainsKey('name') -and $fields['name'] -ne $exename) { $parts += "from=$($fields['name'])" } + #integrity flag: a runtime filed under a platform folder its metadata says it + #was not built for (cross-platform fetch/staging misfiling) + if ($expectedplatform -ne "" -and $fields.ContainsKey('target') -and $fields['target'] -ne $expectedplatform) { + $parts += "!TARGET-MISMATCH:$($fields['target'])" + } if ($parts.Count -eq 0) { return "" } @@ -2147,8 +2377,19 @@ function psmain { $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( 'remote', [switch], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to list for + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "listruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('remote', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'fetch' -or $action -eq 'use') { #GetDynamicParamDictionary ParameterDefinitions @@ -2164,8 +2405,21 @@ function psmain { 'runtime', [string], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to fetch + #into / select within (default: the local platform) + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "fetchruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('runtime', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'run') { #GetDynamicParamDictionary ParameterDefinitions @@ -2210,7 +2464,7 @@ function psmain { 'action' { write-host "got action " $PSBoundParameters.action Set-Variable -Name $_ -Value $PSBoundParameters."$_" - $known_actions = @("fetch", "list", "use", "run") + $known_actions = @("fetch", "list", "use", "run", "help") if (-not($known_actions -contains $action)) { write-host "action '$action' not understood. Known_actions: $known_actions" exit 1 @@ -2242,16 +2496,25 @@ function psmain { } switch ($action) { 'fetch' { - $arch = "win32-x86_64" - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $archurl = "$artifacturl/$arch" $sha1url = "$archurl/sha1sums.txt" - $runtime = "tclsh902z.exe" foreach ($boundparam in $PSBoundParameters.Keys) { write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" } + $runtime = "" if ( $PSBoundParameters["runtime"].Length ) { $runtime = $PSBoundParameters["runtime"] + } elseif ($arch -eq $script:PunkLocalPlatform) { + $runtime = "tclsh902z.exe" + } else { + #fetch-name defaults only make sense for the platform we are standing + #on - a foreign-platform fetch (cross-build staging) names its runtime + write-host "No default runtime for foreign platform '$arch' - name it explicitly:" + write-host " punk-runtime.cmd fetch -platform $arch" + write-host " (see available names: punk-runtime.cmd list -remote -platform $arch)" + exit 1 } $fileurl = "$archurl/$runtime" @@ -2363,8 +2626,11 @@ function psmain { } } 'use' { - #select the active runtime for subsequent 'run' calls - $arch = "win32-x86_64" + #select the active runtime for subsequent 'run' calls. With -platform: + #manage THAT platform folder's selection/materialization (cross-build + #staging - the active.toml travels with the folder when deployed; 'run' + #on this machine only ever consults the local platform). + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $candidates = Get-PunkRuntimeCandidates $archfolder $rtname = "" @@ -2372,8 +2638,8 @@ function psmain { $rtname = $PSBoundParameters["runtime"] } if ($rtname -eq "") { - write-host "Usage: punk-runtime.cmd use " - write-host "Installed candidates:" + write-host "Usage: punk-runtime.cmd use ?-platform

?" + 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 with local-platform + default (PUNK_RUNTIME_PLATFORM env override), foreign fetch name-required, + list !TARGET-MISMATCH integrity flag from the G-103 metadata target field, + run local-only. Platform-dir naming alignment progress: bash payload's + FreeBSD dir aligned to punkbin's actual freebsd-x86_64 (was freebsd-amd64). + Still open for this goal's alignment item: the zig-triple <-> platform-dir + mapping itself, openbsd-amd64 (payload) vs the x86_64 convention, and a + windows-arm default for the ps1 payload once punkbin carries such a folder. diff --git a/punkproject.toml b/punkproject.toml index f2f6627e..9aa8b82f 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.17.8" +version = "0.17.9" license = "BSD-2-Clause" diff --git a/src/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd b/src/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd index 1f713340..88b619ed 100644 --- a/src/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd +++ b/src/project_layouts/vendor/punk/project-0.1/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 ?? ?-platform

? download+verify a runtime from punkbin" + echo " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" + echo " use ?-platform

? select active / materialize -r artifact" + echo " run ?args...? launch the active local-platform runtime" + echo " help full help (options, env vars, examples)" +} +show_help() { + echo "punk-runtime - manage the plain Tcl runtimes under bin/runtime//" + echo "" + show_usage + echo "" + echo "Actions:" + echo " fetch ?? ?-platform

?" + echo " Download a runtime from the punkbin artifact server into" + echo " bin/runtime/

/ with sha1 verification against the server's" + echo " sha1sums.txt. -r-named family artifacts also fetch their" + echo " .toml metadata record. Omitting fetches the" + echo " local platform's default runtime; a FOREIGN platform fetch" + echo " requires an explicit ." + echo " list ?-remote? ?-platform

?" + 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 ?-platform

?" + echo " Select the runtime that 'run' launches (records active.toml in" + echo " the platform folder). An immutable -r ARTIFACT name is" + echo " MATERIALIZED into its WORKING name (name minus -r - what" + echo " mapvfs and projects reference), metadata toml copied alongside." + echo " run ?args...?" + echo " Launch the ACTIVE runtime of the LOCAL platform, passing args." + echo " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole" + echo " installed candidate. Takes no -platform (foreign binaries are" + echo " not runnable here)." + echo "" + echo "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64," + echo " macosx) - not zig triples. Default is the local platform; override" + echo " order: -platform argument > PUNK_RUNTIME_PLATFORM env var." + echo " Foreign-platform folders serve cross-build staging and provisioning:" + echo " active.toml travels with the folder when it is deployed to its real" + echo " platform (unix exec bits are restored at deploy time - e.g rsync/tar/" + echo " chmod on the receiving side)." + echo "" + echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," + echo " PUNK_ACTIVE_RUNTIME (run-time selection override)" + echo "" + echo "Examples:" + echo " $0 fetch tclsh9.0.5-punk-r1.exe" + echo " $0 use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)" + echo " $0 list -remote -platform linux-x86_64" + echo " $0 fetch tclsh9.0.5-punk-r1 -platform linux-x86_64" + echo " $0 use tclsh9.0.5-punk-r1 -platform linux-x86_64" + echo " $0 run myscript.tcl arg1 arg2" +} #G-103 artifact-tier names carry -r before the (optional) .exe suffix; prints #the WORKING name (artifact minus -r) when the name is artifact-tier, else "" working_name_of() { @@ -1451,13 +1563,20 @@ working_name_of() { if [[ "$w" != "$1" ]]; then printf '%s' "$w"; fi } -case "$1" in +case "$action" in "fetch") runtime="$rt_default" - if [[ -n "$2" ]]; then - runtime="$2" + if [[ -n "${1:-}" ]]; then + runtime="$1" + elif [[ "$is_local" -ne 1 ]]; then + #fetch-name defaults only make sense for the platform we are standing + #on - a foreign-platform fetch (cross-build staging) names its runtime + echo "No default runtime for foreign platform '$platform' - name it explicitly:" + echo " $0 fetch -platform $platform" + echo " (see available names: $0 list -remote -platform $platform)" + exit 1 fi - if [[ ( "$runtime_available" -eq 1 || -n "$2" ) && "$archtail" != "other" && -n "$runtime" ]]; then + if [[ ( "$runtime_available" -eq 1 || -n "${1:-}" ) && "$archtail" != "other" && -n "$runtime" ]]; then url="${url_kitbase}/${archtail}/${runtime}" output="${archdir}/${runtime}" sha1url="${url_kitbase}/${archtail}/sha1sums.txt" @@ -1547,7 +1666,7 @@ case "$1" in fi ;; "list") - if [[ "$2" == "-remote" ]]; then + if [[ "${1:-}" == "-remote" ]]; then #compare local runtimes with those listed on the artifact server #(functional parity with the powershell payload's 'list -remote') sha1url="${url_kitbase}/${archtail}/sha1sums.txt" @@ -1620,9 +1739,12 @@ case "$1" in fi ;; "use") - if [[ -z "$2" ]]; then - echo "Usage: $0 use " - echo "Installed candidates:" + #with -platform: manage THAT platform folder's selection/materialization + #(cross-build staging - active.toml travels with the folder when deployed; + #'run' on this machine only ever consults the local platform) + if [[ -z "${1:-}" ]]; then + echo "Usage: $0 use ?-platform

?" + 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 - what mapvfs and #projects reference), copies its metadata toml alongside, and selects #the working name. Republishing artifacts never churns consumers. - working=$(working_name_of "$2") + #(chmod is a no-op when staging unix runtimes from windows filesystems - + #exec bits are restored at deploy time, e.g rsync/tar/chmod on the guest.) + working=$(working_name_of "$1") if [[ -n "$working" ]]; then - cp -f "$archdir/$2" "$archdir/$working" + cp -f "$archdir/$1" "$archdir/$working" chmod +x "$archdir/$working" - srctoml="$archdir/$(rootname_of "$2").toml" + srctoml="$archdir/$(rootname_of "$1").toml" desttoml="$archdir/$(rootname_of "$working").toml" if [[ -f "$srctoml" ]]; then cp -f "$srctoml" "$desttoml" - echo "materialized $working from artifact $2 (metadata toml copied alongside)" + echo "materialized $working from artifact $1 (metadata toml copied alongside)" else - echo "materialized $working from artifact $2 (no metadata toml found beside the artifact)" + echo "materialized $working from artifact $1 (no metadata toml found beside the artifact)" fi set_active "$working" else - set_active "$2" + set_active "$1" fi ;; "run") #resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single - #installed candidate - otherwise error with candidates (no last-in-list guessing) + #installed candidate - otherwise error with candidates (no last-in-list guessing). + #LOCAL platform only: the -platform arg is rejected in the scan above, and a + #PUNK_RUNTIME_PLATFORM env override is likewise ignored here (foreign + #binaries are not runnable; the env override serves list/fetch/use). + if [[ "$is_local" -ne 1 ]]; then + archdir="${scriptdir}/runtime/${local_platform}" + active_file="${archdir}/active.toml" + fi activeruntime="" if [[ -n "$PUNK_ACTIVE_RUNTIME" ]]; then activeruntime="$PUNK_ACTIVE_RUNTIME" @@ -1706,13 +1837,22 @@ case "$1" in fi activeruntime_fullpath="$archdir/$activeruntime" #echo "using $activeruntime_fullpath" - shift - #echo "args: $@" + #(the action was already shifted off during the option scan - "$@" is + #exactly the runtime's argument list) $activeruntime_fullpath "$@" ;; + "help") + show_help + ;; + "") + #no action: short usage + pointer at the fuller help + show_usage + echo "" + echo "'$0 help' gives options, env vars and examples." + ;; *) - echo "Usage: $0 {fetch|list|use|run}" - echo "received $@" + echo "unknown action '$action'" + show_usage exit 1 ;; esac @@ -2085,6 +2225,91 @@ function Get-PunkRuntimeCandidates { } return @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not ($(".txt",".toml",".tm",".tmp",".log") -contains $_.Extension) } | Sort-Object Name) } +#operator help (the 'help' action; the no-args case shows the Usage block only). +#Keep in sync with the bash payload's show_help/show_usage. +function Show-PunkRuntimeUsage { + write-host "Usage: punk-runtime.cmd {fetch|list|use|run|help}" + write-host " fetch ?? ?-platform

? download+verify a runtime from punkbin" + write-host " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" + write-host " use ?-platform

? select active / materialize -r artifact" + write-host " run ?args...? launch the active local-platform runtime" + write-host " help full help (options, env vars, examples)" +} +function Show-PunkRuntimeHelp { + write-host "punk-runtime - manage the plain Tcl runtimes under bin/runtime//" + write-host "" + Show-PunkRuntimeUsage + write-host "" + write-host "Actions:" + write-host " fetch ?? ?-platform

?" + 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-named family artifacts also fetch their" + write-host " .toml metadata record. Omitting fetches the" + write-host " local platform's default runtime; a FOREIGN platform fetch" + write-host " requires an explicit ." + write-host " list ?-remote? ?-platform

?" + 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 ?-platform

?" + write-host " Select the runtime that 'run' launches (records active.toml in" + write-host " the platform folder). An immutable -r ARTIFACT name is" + write-host " MATERIALIZED into its WORKING name (name minus -r - what" + write-host " mapvfs and projects reference), metadata toml copied alongside." + write-host " run ?args...?" + write-host " Launch the ACTIVE runtime of the LOCAL platform, passing args." + write-host " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole" + write-host " installed candidate. Takes no -platform (foreign binaries are" + write-host " not runnable here)." + write-host "" + write-host "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64," + write-host " macosx) - not zig triples. Default is the local platform; override" + write-host " order: -platform argument > PUNK_RUNTIME_PLATFORM env var." + write-host " Foreign-platform folders serve cross-build staging and provisioning:" + write-host " active.toml travels with the folder when it is deployed to its real" + write-host " platform (unix exec bits are restored at deploy time - e.g rsync/tar/" + write-host " chmod on the receiving side)." + write-host "" + write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," + write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)" + write-host "" + write-host "Examples:" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1.exe" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)" + write-host " punk-runtime.cmd list -remote -platform linux-x86_64" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd run myscript.tcl arg1 arg2" +} +#platform (target) resolution for fetch/list/use: -platform arg > PUNK_RUNTIME_PLATFORM +#env > the local default. Values are punkbin/bin-runtime 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; punk-runtime speaks only the punkbin tier). Validation is +#deliberately shape-only: the server's sha1sums/404 is the truth for what exists. +#This payload runs on windows - the local default stays win32-x86_64 (a windows-arm +#default becomes a question when punkbin carries such a folder). +$script:PunkLocalPlatform = "win32-x86_64" +function Resolve-PunkRuntimePlatform { + param([string] $requested) + $plat = $script:PunkLocalPlatform + if ($requested) { + $plat = $requested + } elseif ($env:PUNK_RUNTIME_PLATFORM) { + $plat = $env:PUNK_RUNTIME_PLATFORM + } + #-cnotmatch: platform-dir names are lowercase and the artifact-server URL path + #is case-sensitive (default -notmatch is case-insensitive and would pass typos + #like Win32-X86_64 through to a server 404); bash payload parity (=~ is + #case-sensitive there) + if ($plat -cnotmatch '^[a-z0-9][a-z0-9_-]*$') { + write-host "invalid platform name '$plat' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)" + exit 1 + } + return $plat +} #name minus a .exe suffix only - dotted tcl patchlevels (tclsh9.0.5-punk) make #generic last-dot extension stripping wrong for extensionless unix names function Get-PunkRuntimeRootName { @@ -2098,7 +2323,7 @@ function Get-PunkRuntimeRootName { #by the buildsuite kit-family-artifacts step / fetched from punkbin). Returns a #short "[variant=... tcl=... rN ...]" summary for list output, "" when absent. function Get-PunkRuntimeMetadataSummary { - param([string] $archfolder, [string] $exename) + param([string] $archfolder, [string] $exename, [string] $expectedplatform = "") $tomlfile = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $exename) + ".toml") if (-not (Test-Path -Path $tomlfile -PathType Leaf)) { return "" @@ -2124,6 +2349,11 @@ function Get-PunkRuntimeMetadataSummary { } #a materialized working copy records which immutable artifact it came from if ($fields.ContainsKey('name') -and $fields['name'] -ne $exename) { $parts += "from=$($fields['name'])" } + #integrity flag: a runtime filed under a platform folder its metadata says it + #was not built for (cross-platform fetch/staging misfiling) + if ($expectedplatform -ne "" -and $fields.ContainsKey('target') -and $fields['target'] -ne $expectedplatform) { + $parts += "!TARGET-MISMATCH:$($fields['target'])" + } if ($parts.Count -eq 0) { return "" } @@ -2147,8 +2377,19 @@ function psmain { $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( 'remote', [switch], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to list for + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "listruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('remote', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'fetch' -or $action -eq 'use') { #GetDynamicParamDictionary ParameterDefinitions @@ -2164,8 +2405,21 @@ function psmain { 'runtime', [string], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to fetch + #into / select within (default: the local platform) + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "fetchruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('runtime', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'run') { #GetDynamicParamDictionary ParameterDefinitions @@ -2210,7 +2464,7 @@ function psmain { 'action' { write-host "got action " $PSBoundParameters.action Set-Variable -Name $_ -Value $PSBoundParameters."$_" - $known_actions = @("fetch", "list", "use", "run") + $known_actions = @("fetch", "list", "use", "run", "help") if (-not($known_actions -contains $action)) { write-host "action '$action' not understood. Known_actions: $known_actions" exit 1 @@ -2242,16 +2496,25 @@ function psmain { } switch ($action) { 'fetch' { - $arch = "win32-x86_64" - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $archurl = "$artifacturl/$arch" $sha1url = "$archurl/sha1sums.txt" - $runtime = "tclsh902z.exe" foreach ($boundparam in $PSBoundParameters.Keys) { write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" } + $runtime = "" if ( $PSBoundParameters["runtime"].Length ) { $runtime = $PSBoundParameters["runtime"] + } elseif ($arch -eq $script:PunkLocalPlatform) { + $runtime = "tclsh902z.exe" + } else { + #fetch-name defaults only make sense for the platform we are standing + #on - a foreign-platform fetch (cross-build staging) names its runtime + write-host "No default runtime for foreign platform '$arch' - name it explicitly:" + write-host " punk-runtime.cmd fetch -platform $arch" + write-host " (see available names: punk-runtime.cmd list -remote -platform $arch)" + exit 1 } $fileurl = "$archurl/$runtime" @@ -2363,8 +2626,11 @@ function psmain { } } 'use' { - #select the active runtime for subsequent 'run' calls - $arch = "win32-x86_64" + #select the active runtime for subsequent 'run' calls. With -platform: + #manage THAT platform folder's selection/materialization (cross-build + #staging - the active.toml travels with the folder when deployed; 'run' + #on this machine only ever consults the local platform). + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $candidates = Get-PunkRuntimeCandidates $archfolder $rtname = "" @@ -2372,8 +2638,8 @@ function psmain { $rtname = $PSBoundParameters["runtime"] } if ($rtname -eq "") { - write-host "Usage: punk-runtime.cmd use " - write-host "Installed candidates:" + write-host "Usage: punk-runtime.cmd use ?-platform

?" + 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 ?? ?-platform

? download+verify a runtime from punkbin" + echo " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" + echo " use ?-platform

? select active / materialize -r artifact" + echo " run ?args...? launch the active local-platform runtime" + echo " help full help (options, env vars, examples)" +} +show_help() { + echo "punk-runtime - manage the plain Tcl runtimes under bin/runtime//" + echo "" + show_usage + echo "" + echo "Actions:" + echo " fetch ?? ?-platform

?" + echo " Download a runtime from the punkbin artifact server into" + echo " bin/runtime/

/ with sha1 verification against the server's" + echo " sha1sums.txt. -r-named family artifacts also fetch their" + echo " .toml metadata record. Omitting fetches the" + echo " local platform's default runtime; a FOREIGN platform fetch" + echo " requires an explicit ." + echo " list ?-remote? ?-platform

?" + 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 ?-platform

?" + echo " Select the runtime that 'run' launches (records active.toml in" + echo " the platform folder). An immutable -r ARTIFACT name is" + echo " MATERIALIZED into its WORKING name (name minus -r - what" + echo " mapvfs and projects reference), metadata toml copied alongside." + echo " run ?args...?" + echo " Launch the ACTIVE runtime of the LOCAL platform, passing args." + echo " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole" + echo " installed candidate. Takes no -platform (foreign binaries are" + echo " not runnable here)." + echo "" + echo "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64," + echo " macosx) - not zig triples. Default is the local platform; override" + echo " order: -platform argument > PUNK_RUNTIME_PLATFORM env var." + echo " Foreign-platform folders serve cross-build staging and provisioning:" + echo " active.toml travels with the folder when it is deployed to its real" + echo " platform (unix exec bits are restored at deploy time - e.g rsync/tar/" + echo " chmod on the receiving side)." + echo "" + echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," + echo " PUNK_ACTIVE_RUNTIME (run-time selection override)" + echo "" + echo "Examples:" + echo " $0 fetch tclsh9.0.5-punk-r1.exe" + echo " $0 use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)" + echo " $0 list -remote -platform linux-x86_64" + echo " $0 fetch tclsh9.0.5-punk-r1 -platform linux-x86_64" + echo " $0 use tclsh9.0.5-punk-r1 -platform linux-x86_64" + echo " $0 run myscript.tcl arg1 arg2" +} #G-103 artifact-tier names carry -r before the (optional) .exe suffix; prints #the WORKING name (artifact minus -r) when the name is artifact-tier, else "" working_name_of() { @@ -171,13 +283,20 @@ working_name_of() { if [[ "$w" != "$1" ]]; then printf '%s' "$w"; fi } -case "$1" in +case "$action" in "fetch") runtime="$rt_default" - if [[ -n "$2" ]]; then - runtime="$2" + if [[ -n "${1:-}" ]]; then + runtime="$1" + elif [[ "$is_local" -ne 1 ]]; then + #fetch-name defaults only make sense for the platform we are standing + #on - a foreign-platform fetch (cross-build staging) names its runtime + echo "No default runtime for foreign platform '$platform' - name it explicitly:" + echo " $0 fetch -platform $platform" + echo " (see available names: $0 list -remote -platform $platform)" + exit 1 fi - if [[ ( "$runtime_available" -eq 1 || -n "$2" ) && "$archtail" != "other" && -n "$runtime" ]]; then + if [[ ( "$runtime_available" -eq 1 || -n "${1:-}" ) && "$archtail" != "other" && -n "$runtime" ]]; then url="${url_kitbase}/${archtail}/${runtime}" output="${archdir}/${runtime}" sha1url="${url_kitbase}/${archtail}/sha1sums.txt" @@ -267,7 +386,7 @@ case "$1" in fi ;; "list") - if [[ "$2" == "-remote" ]]; then + if [[ "${1:-}" == "-remote" ]]; then #compare local runtimes with those listed on the artifact server #(functional parity with the powershell payload's 'list -remote') sha1url="${url_kitbase}/${archtail}/sha1sums.txt" @@ -340,9 +459,12 @@ case "$1" in fi ;; "use") - if [[ -z "$2" ]]; then - echo "Usage: $0 use " - echo "Installed candidates:" + #with -platform: manage THAT platform folder's selection/materialization + #(cross-build staging - active.toml travels with the folder when deployed; + #'run' on this machine only ever consults the local platform) + if [[ -z "${1:-}" ]]; then + echo "Usage: $0 use ?-platform

?" + 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 - what mapvfs and #projects reference), copies its metadata toml alongside, and selects #the working name. Republishing artifacts never churns consumers. - working=$(working_name_of "$2") + #(chmod is a no-op when staging unix runtimes from windows filesystems - + #exec bits are restored at deploy time, e.g rsync/tar/chmod on the guest.) + working=$(working_name_of "$1") if [[ -n "$working" ]]; then - cp -f "$archdir/$2" "$archdir/$working" + cp -f "$archdir/$1" "$archdir/$working" chmod +x "$archdir/$working" - srctoml="$archdir/$(rootname_of "$2").toml" + srctoml="$archdir/$(rootname_of "$1").toml" desttoml="$archdir/$(rootname_of "$working").toml" if [[ -f "$srctoml" ]]; then cp -f "$srctoml" "$desttoml" - echo "materialized $working from artifact $2 (metadata toml copied alongside)" + echo "materialized $working from artifact $1 (metadata toml copied alongside)" else - echo "materialized $working from artifact $2 (no metadata toml found beside the artifact)" + echo "materialized $working from artifact $1 (no metadata toml found beside the artifact)" fi set_active "$working" else - set_active "$2" + set_active "$1" fi ;; "run") #resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single - #installed candidate - otherwise error with candidates (no last-in-list guessing) + #installed candidate - otherwise error with candidates (no last-in-list guessing). + #LOCAL platform only: the -platform arg is rejected in the scan above, and a + #PUNK_RUNTIME_PLATFORM env override is likewise ignored here (foreign + #binaries are not runnable; the env override serves list/fetch/use). + if [[ "$is_local" -ne 1 ]]; then + archdir="${scriptdir}/runtime/${local_platform}" + active_file="${archdir}/active.toml" + fi activeruntime="" if [[ -n "$PUNK_ACTIVE_RUNTIME" ]]; then activeruntime="$PUNK_ACTIVE_RUNTIME" @@ -426,13 +557,22 @@ case "$1" in fi activeruntime_fullpath="$archdir/$activeruntime" #echo "using $activeruntime_fullpath" - shift - #echo "args: $@" + #(the action was already shifted off during the option scan - "$@" is + #exactly the runtime's argument list) $activeruntime_fullpath "$@" ;; + "help") + show_help + ;; + "") + #no action: short usage + pointer at the fuller help + show_usage + echo "" + echo "'$0 help' gives options, env vars and examples." + ;; *) - echo "Usage: $0 {fetch|list|use|run}" - echo "received $@" + echo "unknown action '$action'" + show_usage exit 1 ;; esac diff --git a/src/scriptapps/bin/punk-runtime.ps1 b/src/scriptapps/bin/punk-runtime.ps1 index 38b5d655..6b4df331 100644 --- a/src/scriptapps/bin/punk-runtime.ps1 +++ b/src/scriptapps/bin/punk-runtime.ps1 @@ -70,6 +70,91 @@ function Get-PunkRuntimeCandidates { } return @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not ($(".txt",".toml",".tm",".tmp",".log") -contains $_.Extension) } | Sort-Object Name) } +#operator help (the 'help' action; the no-args case shows the Usage block only). +#Keep in sync with the bash payload's show_help/show_usage. +function Show-PunkRuntimeUsage { + write-host "Usage: punk-runtime.cmd {fetch|list|use|run|help}" + write-host " fetch ?? ?-platform

? download+verify a runtime from punkbin" + write-host " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" + write-host " use ?-platform

? select active / materialize -r artifact" + write-host " run ?args...? launch the active local-platform runtime" + write-host " help full help (options, env vars, examples)" +} +function Show-PunkRuntimeHelp { + write-host "punk-runtime - manage the plain Tcl runtimes under bin/runtime//" + write-host "" + Show-PunkRuntimeUsage + write-host "" + write-host "Actions:" + write-host " fetch ?? ?-platform

?" + 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-named family artifacts also fetch their" + write-host " .toml metadata record. Omitting fetches the" + write-host " local platform's default runtime; a FOREIGN platform fetch" + write-host " requires an explicit ." + write-host " list ?-remote? ?-platform

?" + 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 ?-platform

?" + write-host " Select the runtime that 'run' launches (records active.toml in" + write-host " the platform folder). An immutable -r ARTIFACT name is" + write-host " MATERIALIZED into its WORKING name (name minus -r - what" + write-host " mapvfs and projects reference), metadata toml copied alongside." + write-host " run ?args...?" + write-host " Launch the ACTIVE runtime of the LOCAL platform, passing args." + write-host " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole" + write-host " installed candidate. Takes no -platform (foreign binaries are" + write-host " not runnable here)." + write-host "" + write-host "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64," + write-host " macosx) - not zig triples. Default is the local platform; override" + write-host " order: -platform argument > PUNK_RUNTIME_PLATFORM env var." + write-host " Foreign-platform folders serve cross-build staging and provisioning:" + write-host " active.toml travels with the folder when it is deployed to its real" + write-host " platform (unix exec bits are restored at deploy time - e.g rsync/tar/" + write-host " chmod on the receiving side)." + write-host "" + write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," + write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)" + write-host "" + write-host "Examples:" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1.exe" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)" + write-host " punk-runtime.cmd list -remote -platform linux-x86_64" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd run myscript.tcl arg1 arg2" +} +#platform (target) resolution for fetch/list/use: -platform arg > PUNK_RUNTIME_PLATFORM +#env > the local default. Values are punkbin/bin-runtime 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; punk-runtime speaks only the punkbin tier). Validation is +#deliberately shape-only: the server's sha1sums/404 is the truth for what exists. +#This payload runs on windows - the local default stays win32-x86_64 (a windows-arm +#default becomes a question when punkbin carries such a folder). +$script:PunkLocalPlatform = "win32-x86_64" +function Resolve-PunkRuntimePlatform { + param([string] $requested) + $plat = $script:PunkLocalPlatform + if ($requested) { + $plat = $requested + } elseif ($env:PUNK_RUNTIME_PLATFORM) { + $plat = $env:PUNK_RUNTIME_PLATFORM + } + #-cnotmatch: platform-dir names are lowercase and the artifact-server URL path + #is case-sensitive (default -notmatch is case-insensitive and would pass typos + #like Win32-X86_64 through to a server 404); bash payload parity (=~ is + #case-sensitive there) + if ($plat -cnotmatch '^[a-z0-9][a-z0-9_-]*$') { + write-host "invalid platform name '$plat' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)" + exit 1 + } + return $plat +} #name minus a .exe suffix only - dotted tcl patchlevels (tclsh9.0.5-punk) make #generic last-dot extension stripping wrong for extensionless unix names function Get-PunkRuntimeRootName { @@ -83,7 +168,7 @@ function Get-PunkRuntimeRootName { #by the buildsuite kit-family-artifacts step / fetched from punkbin). Returns a #short "[variant=... tcl=... rN ...]" summary for list output, "" when absent. function Get-PunkRuntimeMetadataSummary { - param([string] $archfolder, [string] $exename) + param([string] $archfolder, [string] $exename, [string] $expectedplatform = "") $tomlfile = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $exename) + ".toml") if (-not (Test-Path -Path $tomlfile -PathType Leaf)) { return "" @@ -109,6 +194,11 @@ function Get-PunkRuntimeMetadataSummary { } #a materialized working copy records which immutable artifact it came from if ($fields.ContainsKey('name') -and $fields['name'] -ne $exename) { $parts += "from=$($fields['name'])" } + #integrity flag: a runtime filed under a platform folder its metadata says it + #was not built for (cross-platform fetch/staging misfiling) + if ($expectedplatform -ne "" -and $fields.ContainsKey('target') -and $fields['target'] -ne $expectedplatform) { + $parts += "!TARGET-MISMATCH:$($fields['target'])" + } if ($parts.Count -eq 0) { return "" } @@ -132,8 +222,19 @@ function psmain { $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( 'remote', [switch], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to list for + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "listruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('remote', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'fetch' -or $action -eq 'use') { #GetDynamicParamDictionary ParameterDefinitions @@ -149,8 +250,21 @@ function psmain { 'runtime', [string], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to fetch + #into / select within (default: the local platform) + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "fetchruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('runtime', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'run') { #GetDynamicParamDictionary ParameterDefinitions @@ -195,7 +309,7 @@ function psmain { 'action' { write-host "got action " $PSBoundParameters.action Set-Variable -Name $_ -Value $PSBoundParameters."$_" - $known_actions = @("fetch", "list", "use", "run") + $known_actions = @("fetch", "list", "use", "run", "help") if (-not($known_actions -contains $action)) { write-host "action '$action' not understood. Known_actions: $known_actions" exit 1 @@ -227,16 +341,25 @@ function psmain { } switch ($action) { 'fetch' { - $arch = "win32-x86_64" - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $archurl = "$artifacturl/$arch" $sha1url = "$archurl/sha1sums.txt" - $runtime = "tclsh902z.exe" foreach ($boundparam in $PSBoundParameters.Keys) { write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" } + $runtime = "" if ( $PSBoundParameters["runtime"].Length ) { $runtime = $PSBoundParameters["runtime"] + } elseif ($arch -eq $script:PunkLocalPlatform) { + $runtime = "tclsh902z.exe" + } else { + #fetch-name defaults only make sense for the platform we are standing + #on - a foreign-platform fetch (cross-build staging) names its runtime + write-host "No default runtime for foreign platform '$arch' - name it explicitly:" + write-host " punk-runtime.cmd fetch -platform $arch" + write-host " (see available names: punk-runtime.cmd list -remote -platform $arch)" + exit 1 } $fileurl = "$archurl/$runtime" @@ -348,8 +471,11 @@ function psmain { } } 'use' { - #select the active runtime for subsequent 'run' calls - $arch = "win32-x86_64" + #select the active runtime for subsequent 'run' calls. With -platform: + #manage THAT platform folder's selection/materialization (cross-build + #staging - the active.toml travels with the folder when deployed; 'run' + #on this machine only ever consults the local platform). + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $candidates = Get-PunkRuntimeCandidates $archfolder $rtname = "" @@ -357,8 +483,8 @@ function psmain { $rtname = $PSBoundParameters["runtime"] } if ($rtname -eq "") { - write-host "Usage: punk-runtime.cmd use " - write-host "Installed candidates:" + write-host "Usage: punk-runtime.cmd use ?-platform

?" + 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." } }