Browse Source

G-139 phase 2 increment 1: PACKAGES_tcl<N> deployment + vfslibs packages-root declarations (0.31.1)

make.tcl libs/packages/bakehouse gain the PACKAGES_tcl<N> phase: materialized
punkbin lib-tier packages under bin/packages/<target>/tcl<N>/ deploy into
lib_tcl<N>/allplatforms + lib_tcl<N>/<this platform> with punkcheck
provenance, mirroring the VENDORLIB_tcl<N> shape (embedded
punkbin-artifact.toml records ride into the deployed trees; absent tier =
silent per-item no-op with a libfetch pointer).

vfslibs declaration schema extended (vendorlib_vfs.toml header documents
both): source_root = "packages" resolves an entry's source against the
bin/packages tier (missing source errors name 'make.tcl libfetch');
replace = true clean-slates a SAME-NAMED existing target folder before
install (supersedes deliberately cannot name the folder being installed -
needed because the pre-G-139 tcl9 hand-drops nested md5c + tcllibc INSIDE
tcllib2.0).

Declarations added for the switchover set (per-kit, never blanket - the
trees that carried hand-drops at 2026-07-30): 8 win32 tcl9 kit vfs trees get
tcllib2.0-tcl9 + tcllibc-tcl9 (supersedes the stray-named tcllibc2.0);
punk9linux (linux target) gets only platform-neutral tcllib2.0 (its
linux-built tcllibc siblings stay - a linux lib-tier emission is future
work); punk86/punk8win/punk8_statictwapi upgrade tcllib1.21 (+nested tcllibc
0.4 + md5c, removed with it via supersedes) to tcllib2.0-tcl8 + tcllibc-tcl8;
punk86bawt refreshes its bare tcllibc only.

Verified: make.tcl libs deployed the tier into lib_tcl8|9 (allplatforms +
win32-x86_64) with embedded records present; vendored mirrors coexist in the
deployed trees until the sequenced retirement. Thin-layout make.tcl sync
copies updated by the run (punkcheck-managed outputs, batched per the
carve-out). Project 0.31.1 + CHANGELOG.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 4 days ago
parent
commit
e7e2ec81f4
  1. 16
      CHANGELOG.md
  2. 2
      punkproject.toml
  3. 71
      src/make.tcl
  4. 455
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl
  5. 455
      src/project_layouts/vendor/punk/basic/src/make.tcl
  6. 455
      src/project_layouts/vendor/punk/project-0.1/src/make.tcl
  7. 86
      src/runtime/vendorlib_vfs.toml

16
CHANGELOG.md

@ -5,6 +5,22 @@ 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` Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy. "Project Versioning" section for the bump policy.
## [0.31.1] - 2026-07-30
- `make.tcl libs`/`packages`/`bakehouse` gain the PACKAGES_tcl<N> phase
(G-139): materialized punkbin lib-tier packages under `bin/packages/`
deploy into `lib_tcl<N>/allplatforms` + `lib_tcl<N>/<platform>` with
punkcheck provenance (silent no-op when the tier is absent). The vfslibs
declaration schema (`src/runtime/vendorlib_vfs.toml`) gains `source_root =
"packages"` (sources resolved against the bin/packages tier, with an
actionable libfetch hint when missing) and `replace = true` (clean-slate
same-name refresh - supersedes deliberately cannot name the folder being
installed). tcllib/tcllibc enter the kit vfs declarations: tcl9 kits
switch their hand-dropped tcllib2.0 (which nested md5c/tcllibc) to the
suite-built artifact pair; tcl8 kits upgrade tcllib1.21 (+nested tcllibc
0.4/md5c) to tcllib 2.0 + current tcllibc via supersedes; punk9linux
(linux target) gets only the platform-neutral tcllib2.0.
## [0.31.0] - 2026-07-30 ## [0.31.0] - 2026-07-30
- New `make.tcl libfetch` subcommand (G-139 phase 1): fetches the punkbin - New `make.tcl libfetch` subcommand (G-139 phase 1): fetches the punkbin

2
punkproject.toml

@ -1,4 +1,4 @@
[project] [project]
name = "punkshell" name = "punkshell"
version = "0.31.0" version = "0.31.1"
license = "BSD-2-Clause" license = "BSD-2-Clause"

71
src/make.tcl

@ -5618,6 +5618,36 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)" puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)"
} }
#G-139: punkbin lib-tier packages from the bin/packages input tier (populated
#by 'make.tcl libfetch') into the deployed lib_tcl<N> trees - allplatforms +
#this host's platform dir, mirroring the vendorlib_tcl<N> shape above. The
#materialized trees are self-describing (embedded punkbin-artifact.toml
#records - G-138) and the records ride along into the deployed trees, and
#from there into kit vfs payloads via the vfslibs declarations. An absent
#tier (or target/generation subdir) is a silent per-item no-op: the tier is
#fetch-regenerable and optional until artifacts are declared and fetched.
set packages_root $projectroot/bin/packages
if {[file isdirectory $packages_root]} {
foreach ptarget [list allplatforms $this_platform_generic] {
foreach gendir [lsort [glob -nocomplain -dir $packages_root/$ptarget -type d tcl*]] {
set which [file tail $gendir] ;#tcl8|tcl9 ...
foreach pkgdir [lsort [glob -nocomplain -dir $gendir -type d *]] {
set pkgtail [file tail $pkgdir]
set target_lib_folder $projectroot/lib_$which/$ptarget
file mkdir $target_lib_folder
puts stdout "PACKAGES_$which: copying $ptarget/$which/$pkgtail from the bin/packages tier to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $pkgdir $target_lib_folder/$pkgtail\
-createdir 1\
-overwrite installedsourcechanged-targets\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
} else {
puts stdout "PACKAGES: no bin/packages tier at $packages_root (populate via 'make.tcl libfetch') - skipping lib-tier package deployment"
}
} }
if {$::punkboot::command in {bakehouse bake vfslibs}} { if {$::punkboot::command in {bakehouse bake vfslibs}} {
@ -5672,9 +5702,42 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
if {[dict exists $ientry supersedes]} { if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]] set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
} }
set source_pkg_folder $sourcefolder/$isource #source_root (G-139): 'src' (default - the original vendorlib
#declarations) or 'packages' - resolve against the untracked
#bin/packages lib-tier input (populated by 'make.tcl libfetch').
set isource_root "src"
if {[dict exists $ientry source_root]} {
set isource_root [punkboot::lib::toml_untag [dict get $ientry source_root]]
}
#replace (G-139): clean-slate a SAME-NAMED existing target folder
#before install. supersedes deliberately cannot name the folder being
#installed; replace covers same-name refreshes where the old copy may
#carry strays the new tree lacks (e.g. hand-drops nesting md5c/tcllibc
#inside tcllib2.0).
set ireplace 0
if {[dict exists $ientry replace]} {
set ireplace [punkboot::lib::toml_untag [dict get $ientry replace]]
if {![string is boolean -strict $ireplace]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname 'replace' must be boolean (got '$ireplace')$A(RST)"
exit 3
}
}
switch -- $isource_root {
src {
set source_pkg_folder $sourcefolder/$isource
set source_missing_hint ""
}
packages {
set source_pkg_folder $projectroot/bin/packages/$isource
set source_missing_hint " - populate the bin/packages tier first: 'tclsh src/make.tcl libfetch' (declarations in src/runtime/libpackages.toml)"
}
default {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname has unknown source_root '$isource_root' (expected src|packages)$A(RST)"
exit 3
}
}
if {![file isdirectory $source_pkg_folder]} { if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)" puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$source_missing_hint$A(RST)"
exit 3 exit 3
} }
set pkgtail [file tail $source_pkg_folder] set pkgtail [file tail $source_pkg_folder]
@ -5704,6 +5767,10 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
file delete -force $suppath file delete -force $suppath
} }
} }
if {$ireplace && [file isdirectory $target_lib_folder/$pkgtail]} {
puts stdout "VFSLIBS install.$iname: replace=true - removing existing '$pkgtail' from vfs/$t (clean-slate install)"
file delete -force $target_lib_folder/$pkgtail
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)" puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\ set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\ -installer make.tcl\

455
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

@ -187,7 +187,7 @@ namespace eval ::punkboot {
variable foldername [file tail $scriptfolder] variable foldername [file tail $scriptfolder]
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list] variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable help_flags [list -help --help /? -h] variable help_flags [list -help --help /? -h]
variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion workflow buildsuite tool project vfs] variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate libfetch bootsupport vfscommonupdate projectversion workflow buildsuite tool project vfs]
} }
@ -2135,6 +2135,10 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n
append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n
append h " - also runs automatically as a phase of '$scriptname bake' (and therefore of '$scriptname bakehouse')" \n \n append h " - also runs automatically as a phase of '$scriptname bake' (and therefore of '$scriptname bakehouse')" \n \n
append h " $scriptname libfetch ?-serverurl <url>? ?-trust-server? ?-force?" \n
append h " - fetch declared punkbin lib-tier library artifacts (src/runtime/libpackages.toml) into bin/packages/<target>" \n
append h " - sha1-verified against the server's per-target sha1sums.txt; materializes package trees under bin/packages/<target>/tcl<N>" \n
append h " - canonical origin trusted by default; other servers (incl. PUNKBIN_URL overrides) require -trust-server" \n \n
append h " $scriptname vfscommonupdate" \n append h " $scriptname vfscommonupdate" \n
append h " - promotion gate: update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n append h " - promotion gate: update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n
append h " - before calling this (followed by '$scriptname bake') - you can test using '<builtexe>(.exe) dev'" \n append h " - before calling this (followed by '$scriptname bake') - you can test using '<builtexe>(.exe) dev'" \n
@ -2323,6 +2327,26 @@ DIAGRAM 2 - KIT ASSEMBLY DETAIL (the 'make.tcl bake' stage; incl. vfslibs phase)
running shell still holds the old exe [K5] running shell still holds the old exe [K5]
LIBRARY-ARTIFACT FETCH (G-139; input tier so far - lib_tcl<N>/kit consumption follows)
----------------------------------------------------------------------------
src/runtime/libpackages.toml which punkbin LIB-TIER artifacts this checkout
consumes (declared artifact names + targets)
|
| 'make.tcl libfetch' - sha1-verified download of the declared
| <pkg>-<tcl8|tcl9>-r<N>.zip artifacts + .toml sidecars from
| <origin>/lib/<target>/ (canonical punkbin origin trusted by
| default; -serverurl/PUNKBIN_URL override needs -trust-server;
| file:// origins serve local mirrors/fixtures), then
| materialization of each verified zip's package tree
v
bin/packages/<target>/ verified -r<N> zips + toml sidecars (input tier,
untracked - mirrors bin/runtime/<target>)
bin/packages/<target>/tcl<N>/<pkgfolder>/
materialized installed-shape trees, self-describing
via their embedded punkbin-artifact.toml records
KEY / NOTES KEY / NOTES
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
[K1] Source modules carry the literal magic version 999999.0a1.0 in the filename. [K1] Source modules carry the literal magic version 999999.0a1.0 in the filename.
@ -2527,6 +2551,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees"
bin "Install executables from src/bin into <projectdir>/bin, then build kits as for bake" bin "Install executables from src/bin into <projectdir>/bin, then build kits as for bake"
vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config" vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config"
libfetch "Fetch declared punkbin lib-tier library artifacts into bin/packages/<target> (sha1-verified) and materialize the package trees"
bootsupport "Promotion gate: update src/bootsupport modules from built project modules" bootsupport "Promotion gate: update src/bootsupport modules from built project modules"
vfscommonupdate "Promotion gate: replace src/vfs/_vfscommon.vfs modules and libs with the project's built set" vfscommonupdate "Promotion gate: replace src/vfs/_vfscommon.vfs modules and libs with the project's built set"
info "Show the name and base folder of the project to be built" info "Show the name and base folder of the project to be built"
@ -2630,6 +2655,24 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
Also runs automatically as a phase of 'make.tcl bake' (and therefore Also runs automatically as a phase of 'make.tcl bake' (and therefore
of 'make.tcl bakehouse'); this standalone narrowing serves quick of 'make.tcl bakehouse'); this standalone narrowing serves quick
iteration on the declarations." iteration on the declarations."
libfetch
"
Fetch the punkbin LIB-TIER library artifacts declared in
src/runtime/libpackages.toml into the untracked input tier
bin/packages/<target>/ - immutable <pkg>-<tcl8|tcl9>-r<N>.zip
artifacts plus their .toml sidecars, each verified against the
server's per-target sha1sums.txt - then materialize each verified
zip's installed-shape package tree under
bin/packages/<target>/tcl<N>/ (self-describing via the embedded
punkbin-artifact.toml record; G-138/G-139).
Present artifacts that already verify are skipped (-force
re-downloads). The canonical punkbin origin is trusted by
default; -serverurl (or env PUNKBIN_URL) overrides it, and any
NON-canonical origin requires the explicit -trust-server flag
(never an interactive prompt - unattended runs stay unattended).
A file:// origin is supported for local mirrors/fixtures.
Transport for http(s): Tcl http+tls when loadable, else curl,
else PowerShell (windows)."
bin bin
" "
Install executables from src/bin to <projectdir>/bin, then check vfs Install executables from src/bin to <projectdir>/bin, then check vfs
@ -2799,6 +2842,30 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
(packages -> test -> bootsupport/vfscommonupdate -> commit -> bake). (packages -> test -> bootsupport/vfscommonupdate -> commit -> bake).
Pass -dirty-abort 0 to build anyway with PROVENANCE-WARNINGs." Pass -dirty-abort 0 to build anyway with PROVENANCE-WARNINGs."
} }
#libfetch fragments (G-139): consent is keyed to SERVER TRUST (the G-123
#posture) - the canonical punkbin origin needs no acknowledgement, any
#other origin needs the explicit flag. Never an interactive prompt.
variable OPT_SERVERURL {
-serverurl -type string -default "" -help\
"Artifact server base url override (default: the canonical punkbin
origin; env PUNKBIN_URL overrides the default, this flag overrides
both). The lib tier is addressed as <serverurl>/lib/<target>/.
file:// urls are supported for local mirrors and fixtures.
A NON-canonical origin additionally requires -trust-server."
}
variable OPT_TRUSTSERVER {
-trust-server -type none -default 0 -help\
"Acknowledge fetching from a NON-canonical artifact server
(consent keyed to server trust - the interim G-123/G-006
mechanism). Without this flag, a non-canonical -serverurl or
PUNKBIN_URL aborts before any network access. Fetches from the
canonical origin never require it."
}
variable OPT_FORCE {
-force -type none -default 0 -help\
"Re-download and re-materialize even when a present artifact
already verifies against the server sha1sums."
}
#which shared option fragments each subcommand declares #which shared option fragments each subcommand declares
#(shell and help have bespoke definitions below: shell passes trailing args #(shell and help have bespoke definitions below: shell passes trailing args
# through to the repl untouched, help takes a subject value) # through to the repl untouched, help takes a subject value)
@ -2812,6 +2879,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
vfslibs {OPT_DIRTYABORT OPT_CONFIRM} vfslibs {OPT_DIRTYABORT OPT_CONFIRM}
bin {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM} bin {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM}
vendorupdate {OPT_CONFIRM} vendorupdate {OPT_CONFIRM}
libfetch {OPT_SERVERURL OPT_TRUSTSERVER OPT_FORCE}
bootsupport {OPT_DIRTYABORT OPT_CONFIRM} bootsupport {OPT_DIRTYABORT OPT_CONFIRM}
vfscommonupdate {OPT_DIRTYABORT OPT_CONFIRM} vfscommonupdate {OPT_DIRTYABORT OPT_CONFIRM}
info {OPT_CONFIRM} info {OPT_CONFIRM}
@ -2829,6 +2897,9 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
OPT_DIRTYABORT "?-dirty-abort?" OPT_DIRTYABORT "?-dirty-abort?"
OPT_DIRTYABORT_ON "?-dirty-abort 1|0?" OPT_DIRTYABORT_ON "?-dirty-abort 1|0?"
OPT_CONFIRM "?-confirm 0|1?" OPT_CONFIRM "?-confirm 0|1?"
OPT_SERVERURL "?-serverurl <url>?"
OPT_TRUSTSERVER "?-trust-server?"
OPT_FORCE "?-force?"
} }
#per-subcommand positional values (default: none). bake (and its deprecated #per-subcommand positional values (default: none). bake (and its deprecated
#alias vfs) and bakelist take optional kit names (G-121 selective bake). #alias vfs) and bakelist take optional kit names (G-121 selective bake).
@ -2940,7 +3011,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
variable SUBGROUPS { variable SUBGROUPS {
"build & bake" {bakehouse packages modules libs bake vfslibs bin} "build & bake" {bakehouse packages modules libs bake vfslibs bin}
"promotion gates" {bootsupport vfscommonupdate} "promotion gates" {bootsupport vfscommonupdate}
"source maintenance" {vendorupdate} "source maintenance" {vendorupdate libfetch}
"buildsuites" {buildsuite} "buildsuites" {buildsuite}
"vendored tools" {tool} "vendored tools" {tool}
"informational" {info check bakelist projectversion workflow help} "informational" {info check bakelist projectversion workflow help}
@ -2996,6 +3067,9 @@ set help_exitcode 0
set ::punkboot::opt_forcekill 0 set ::punkboot::opt_forcekill 0
set ::punkboot::opt_dirty_abort 0 set ::punkboot::opt_dirty_abort 0
set ::punkboot::opt_confirm 1 set ::punkboot::opt_confirm 1
set ::punkboot::opt_serverurl ""
set ::punkboot::opt_trust_server 0
set ::punkboot::opt_force 0
set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bakelist (empty = all configured) set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bakelist (empty = all configured)
if {$::punkboot::punkargs_ok} { if {$::punkboot::punkargs_ok} {
@ -3046,7 +3120,7 @@ if {$::punkboot::punkargs_ok} {
} }
set ::punkboot::command $subcommand set ::punkboot::command $subcommand
set _opts [dict get $argd opts] set _opts [dict get $argd opts]
foreach {_optname _var} {-k opt_forcekill -dirty-abort opt_dirty_abort -confirm opt_confirm} { foreach {_optname _var} {-k opt_forcekill -dirty-abort opt_dirty_abort -confirm opt_confirm -serverurl opt_serverurl -trust-server opt_trust_server -force opt_force} {
if {[dict exists $_opts $_optname]} { if {[dict exists $_opts $_optname]} {
set ::punkboot::$_var [dict get $_opts $_optname] set ::punkboot::$_var [dict get $_opts $_optname]
} }
@ -3083,6 +3157,12 @@ if {$::punkboot::punkargs_ok} {
switch -- $a { switch -- $a {
-k {set ::punkboot::opt_forcekill 1} -k {set ::punkboot::opt_forcekill 1}
-dirty-abort {set ::punkboot::opt_dirty_abort 1} -dirty-abort {set ::punkboot::opt_dirty_abort 1}
-trust-server {set ::punkboot::opt_trust_server 1}
-force {set ::punkboot::opt_force 1}
-serverurl {
set ::punkboot::opt_serverurl [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end]
}
-confirm { -confirm {
set v [lindex $argsleft 0] set v [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end] set argsleft [lrange $argsleft 1 end]
@ -5121,6 +5201,303 @@ if {$::punkboot::command eq "bootsupport"} {
if {$::punkboot::command eq "libfetch"} {
#G-139 phase 1: fetch the punkbin LIB-TIER artifacts declared in
#src/runtime/libpackages.toml into the untracked bin/packages/<target> input
#tier (mirroring bin/runtime/<target>) with sha1 verification against the
#server's per-target sha1sums.txt, then materialize each verified zip's
#installed-shape package tree under bin/packages/<target>/tcl<N>/ (the tree
#is self-describing via its embedded punkbin-artifact.toml - the G-138
#record). Consumption into lib_tcl<N>/ and kit vfs trees is the remainder
#of G-139 - this entrypoint owns fetch + verify + materialize only.
proc ::punkboot::libfetch_transfer {url outfile} {
#one url -> local file. file:// is served natively (local mirrors,
#fixtures - no network, no external tools). http(s) transports in
#order: Tcl http (+tls for https) when loadable, curl (ubiquitous,
#including stock windows 10+), powershell Invoke-WebRequest (windows).
#Returns the transport used; errors when all fail.
file mkdir [file dirname $outfile]
if {[string match -nocase "file://*" $url]} {
set path [string range $url 7 end]
if {[regexp {^/+([A-Za-z]:/.*)$} $path -> winpath]} {
set path $winpath ;#file:///C:/x -> C:/x
}
if {![file isfile $path]} {
error "file origin has no such file: $path"
}
file copy -force $path $outfile
return "file-copy"
}
set is_https [string match -nocase "https://*" $url]
set tcl_http_ok 0
if {![catch {package require http}]} {
if {!$is_https} {
set tcl_http_ok 1
} elseif {![catch {package require tls}]} {
catch {http::register https 443 [list ::tls::socket -autoservername true]}
set tcl_http_ok 1
}
}
if {$tcl_http_ok} {
set fd [open $outfile wb]
set ok 0
if {![catch {http::geturl $url -channel $fd -binary 1} tok]} {
if {[http::status $tok] eq "ok" && [http::ncode $tok] == 200} {set ok 1}
http::cleanup $tok
}
close $fd
if {$ok} {return "tcl-http"}
file delete -force $outfile
}
set curl [auto_execok curl]
if {$curl ne ""} {
if {![catch {exec {*}$curl --fail -sSL -o $outfile $url 2>@1} curlerr]} {
return "curl"
}
file delete -force $outfile
}
if {$::tcl_platform(platform) eq "windows"} {
set ps [auto_execok powershell]
if {$ps ne ""} {
set pscmd "\$ProgressPreference='SilentlyContinue'; Invoke-WebRequest -Uri '$url' -OutFile '$outfile' -ErrorAction Stop"
if {![catch {exec {*}$ps -NoProfile -NonInteractive -Command $pscmd 2>@1} pserr]} {
return "powershell"
}
file delete -force $outfile
}
}
set detail ""
if {[info exists curlerr]} {set detail " (curl: [string range $curlerr 0 200])"}
error "all transports failed for $url$detail"
}
set libfetch_config $sourcefolder/runtime/libpackages.toml
#Canonical origin: the punkbin raw-file base (the punk-runtime.cmd
#convention; lib tier addressed as <origin>/lib/<target>/...).
#Server-trust consent (G-139, the G-123 posture): the canonical origin
#never gates; ANY other origin - flag or PUNKBIN_URL env - requires the
#explicit -trust-server flag. Never an interactive prompt: unattended
#runs stay unattended, and refusal happens before any network access.
set libfetch_canonical "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
set libfetch_origin $libfetch_canonical
if {[info exists ::env(PUNKBIN_URL)] && $::env(PUNKBIN_URL) ne ""} {
set libfetch_origin $::env(PUNKBIN_URL)
}
if {$::punkboot::opt_serverurl ne ""} {
set libfetch_origin $::punkboot::opt_serverurl
}
set libfetch_origin [string trimright $libfetch_origin /]
if {$libfetch_origin ne $libfetch_canonical && !$::punkboot::opt_trust_server} {
puts stderr "$A(BAD)LIBFETCH: origin '$libfetch_origin' is not the canonical punkbin origin$A(RST)"
puts stderr "LIBFETCH: canonical: $libfetch_canonical"
puts stderr "LIBFETCH: fetching from a non-canonical server requires the explicit -trust-server flag (consent keyed to server trust - G-139/G-123). Nothing was fetched."
exit 3
}
if {![file exists $libfetch_config]} {
puts stdout "LIBFETCH: no config at $libfetch_config - nothing declared, nothing to fetch"
} else {
if {[catch {package require tomlish} tomlish_err]} {
puts stderr "$A(BAD)LIBFETCH: cannot process $libfetch_config - tomlish package unavailable ($tomlish_err)$A(RST)"
exit 3
}
if {[catch {package require sha1} sha1_err]} {
puts stderr "$A(BAD)LIBFETCH: sha1 package unavailable ($sha1_err) - cannot verify artifacts$A(RST)"
exit 3
}
if {[catch {package require punk::zip} punkzip_err]} {
puts stderr "$A(BAD)LIBFETCH: punk::zip package unavailable ($punkzip_err) - cannot materialize package trees$A(RST)"
exit 3
}
set fd [open $libfetch_config r]
fconfigure $fd -encoding utf-8
set libfetch_tomldata [read $fd]
close $fd
if {[catch {tomlish::to_dict [tomlish::from_toml $libfetch_tomldata]} libfetch_dict]} {
puts stderr "$A(BAD)LIBFETCH: failed to parse $libfetch_config\n$libfetch_dict$A(RST)"
exit 3
}
if {![llength [info commands ::punkboot::lib::toml_untag]]} {
#tomlish::to_dict values are type-tagged (also defined by the vfslibs
#phase - that block does not run for the libfetch command)
proc ::punkboot::lib::toml_untag {tagged} {
if {[dict get $tagged type] eq "ARRAY"} {
set plain [list]
foreach el [dict get $tagged value] {
lappend plain [::punkboot::lib::toml_untag $el]
}
return $plain
}
return [dict get $tagged value]
}
}
set libfetch_artifacts [dict create]
if {[dict exists $libfetch_dict artifact]} {
set libfetch_artifacts [dict get $libfetch_dict artifact]
}
if {![dict size $libfetch_artifacts]} {
puts stdout "LIBFETCH: no \[artifact.<name>\] entries found in $libfetch_config"
}
puts stdout "LIBFETCH: origin $libfetch_origin ([dict size $libfetch_artifacts] declared artifact(s))"
set libfetch_sums [dict create] ;#target -> dict(filename -> sha1) from the server's sha1sums.txt
set libfetch_failed 0
set libfetch_actions [list]
dict for {aname aentry} $libfetch_artifacts {
foreach reqkey {name target} {
if {![dict exists $aentry $reqkey]} {
puts stderr "$A(BAD)LIBFETCH: entry artifact.$aname in $libfetch_config is missing required key '$reqkey'$A(RST)"
exit 3
}
}
set artifact [punkboot::lib::toml_untag [dict get $aentry name]]
set target [punkboot::lib::toml_untag [dict get $aentry target]]
if {![regexp {^(.+)-(tcl[0-9]+)-r([0-9]+)\.zip$} $artifact -> pkgfolder generation rev]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname name '$artifact' does not match <pkgfolder>-tcl<N>-r<N>.zip$A(RST)"
exit 3
}
set tierdir $projectroot/bin/packages/$target
file mkdir $tierdir
#server truth per target: lib/<target>/sha1sums.txt (fetched once per
#run per target; the local copy in the tier mirrors the punk-runtime
#store idiom)
if {![dict exists $libfetch_sums $target]} {
set sumsurl "$libfetch_origin/lib/$target/sha1sums.txt"
set sumslocal $tierdir/sha1sums.txt
if {[catch {::punkboot::libfetch_transfer $sumsurl $sumslocal} terr]} {
puts stderr "$A(BAD)LIBFETCH: cannot fetch $sumsurl - $terr$A(RST)"
exit 3
}
set sfd [open $sumslocal r]
set sumsdata [read $sfd]
close $sfd
set sums [dict create]
foreach ln [split $sumsdata \n] {
set ln [string trim $ln]
if {$ln eq ""} {continue}
set sp [string first " " $ln]
if {$sp < 1} {continue}
set h [string range $ln 0 $sp-1]
set tag [string range $ln $sp+1 end]
dict set sums [string range $tag 1 end] [string tolower $h]
}
dict set libfetch_sums $target $sums
}
set sums [dict get $libfetch_sums $target]
if {![dict exists $sums $artifact]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: '$artifact' is not listed in the server's lib/$target/sha1sums.txt$A(RST)"
set libfetch_failed 1
continue
}
set want_sha1 [dict get $sums $artifact]
set ziplocal $tierdir/$artifact
set have_ok 0
if {[file isfile $ziplocal] && !$::punkboot::opt_force} {
if {[string tolower [sha1::sha1 -hex -file $ziplocal]] eq $want_sha1} {
set have_ok 1
}
}
if {!$have_ok} {
set zipurl "$libfetch_origin/lib/$target/$artifact"
set partfile $ziplocal.part
if {[catch {::punkboot::libfetch_transfer $zipurl $partfile} terr]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: fetch failed - $terr$A(RST)"
set libfetch_failed 1
continue
}
set got_sha1 [string tolower [sha1::sha1 -hex -file $partfile]]
if {$got_sha1 ne $want_sha1} {
file delete -force $partfile
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sha1 MISMATCH for $artifact (got $got_sha1, server sha1sums says $want_sha1) - deleted$A(RST)"
set libfetch_failed 1
continue
}
file rename -force $partfile $ziplocal
lappend libfetch_actions "fetched lib/$target/$artifact"
} else {
lappend libfetch_actions "verified-present lib/$target/$artifact"
}
#beside-toml sidecar (also sha1sums-listed by emission)
set sidecar "[string range $artifact 0 end-4].toml"
set sidecarlocal $tierdir/$sidecar
set sidecar_ok 0
if {[dict exists $sums $sidecar]} {
set want_side [dict get $sums $sidecar]
if {[file isfile $sidecarlocal] && !$::punkboot::opt_force} {
if {[string tolower [sha1::sha1 -hex -file $sidecarlocal]] eq $want_side} {
set sidecar_ok 1
}
}
if {!$sidecar_ok} {
if {[catch {::punkboot::libfetch_transfer "$libfetch_origin/lib/$target/$sidecar" $sidecarlocal.part} terr]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sidecar fetch failed - $terr$A(RST)"
set libfetch_failed 1
continue
}
if {[string tolower [sha1::sha1 -hex -file $sidecarlocal.part]] ne $want_side} {
file delete -force $sidecarlocal.part
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sidecar sha1 MISMATCH for $sidecar - deleted$A(RST)"
set libfetch_failed 1
continue
}
file rename -force $sidecarlocal.part $sidecarlocal
}
} else {
puts stderr "LIBFETCH: note - $sidecar not listed in server sha1sums (artifact accepted; sidecar skipped)"
}
#materialize: installed-shape tree under <target>/tcl<N>/<pkgfolder>
#(generation subdir because both generations install identically-named
#folders). Fresh tree per materialization; -mtime restores member
#mtimes so the tree mirrors what was zipped.
set gendir $tierdir/$generation
file mkdir $gendir
set treedir $gendir/$pkgfolder
if {[file isdirectory $treedir] && !$::punkboot::opt_force} {
#re-materialize only when the tree's embedded record no longer
#matches this artifact name (self-describing tier - G-138)
set embrec $treedir/punkbin-artifact.toml
set tree_current 0
if {[file isfile $embrec]} {
set efd [open $embrec r]
set edata [read $efd]
close $efd
if {[regexp -line "^name = \"$artifact\"$" $edata]} {
set tree_current 1
}
}
if {$tree_current} {
lappend libfetch_actions "tree-current $target/$generation/$pkgfolder"
continue
}
}
file delete -force $treedir
#defaults: whole archive, -overwrite 1 -mtime 1 (member mtimes restored)
#-verify 1 (per-member crc32) - and accelerator-eligible (punkzip exe)
if {[catch {punk::zip::unzip $ziplocal $gendir} unzip_err]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: materialization failed - $unzip_err$A(RST)"
file delete -force $treedir
set libfetch_failed 1
continue
}
if {![file isfile $treedir/pkgIndex.tcl] || ![file isfile $treedir/punkbin-artifact.toml]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: materialized tree at $treedir lacks pkgIndex.tcl and/or punkbin-artifact.toml$A(RST)"
set libfetch_failed 1
continue
}
lappend libfetch_actions "materialized $target/$generation/$pkgfolder"
}
foreach act $libfetch_actions {
puts stdout "LIBFETCH: $act"
}
if {$libfetch_failed} {
puts stderr "$A(BAD)LIBFETCH: FAILED - one or more declared artifacts could not be fetched/verified/materialized (see above)$A(RST)"
exit 3
}
puts stdout "LIBFETCH: done ([llength $libfetch_actions] action(s)) -> $projectroot/bin/packages"
}
flush stdout
::exit 0
}
if {$::punkboot::command ni {bakehouse packages modules libs bake vfslibs bin}} { if {$::punkboot::command ni {bakehouse packages modules libs bake vfslibs bin}} {
puts stderr "Command $::punkboot::command not implemented - aborting." puts stderr "Command $::punkboot::command not implemented - aborting."
flush stderr flush stderr
@ -5241,6 +5618,36 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)" puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)"
} }
#G-139: punkbin lib-tier packages from the bin/packages input tier (populated
#by 'make.tcl libfetch') into the deployed lib_tcl<N> trees - allplatforms +
#this host's platform dir, mirroring the vendorlib_tcl<N> shape above. The
#materialized trees are self-describing (embedded punkbin-artifact.toml
#records - G-138) and the records ride along into the deployed trees, and
#from there into kit vfs payloads via the vfslibs declarations. An absent
#tier (or target/generation subdir) is a silent per-item no-op: the tier is
#fetch-regenerable and optional until artifacts are declared and fetched.
set packages_root $projectroot/bin/packages
if {[file isdirectory $packages_root]} {
foreach ptarget [list allplatforms $this_platform_generic] {
foreach gendir [lsort [glob -nocomplain -dir $packages_root/$ptarget -type d tcl*]] {
set which [file tail $gendir] ;#tcl8|tcl9 ...
foreach pkgdir [lsort [glob -nocomplain -dir $gendir -type d *]] {
set pkgtail [file tail $pkgdir]
set target_lib_folder $projectroot/lib_$which/$ptarget
file mkdir $target_lib_folder
puts stdout "PACKAGES_$which: copying $ptarget/$which/$pkgtail from the bin/packages tier to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $pkgdir $target_lib_folder/$pkgtail\
-createdir 1\
-overwrite installedsourcechanged-targets\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
} else {
puts stdout "PACKAGES: no bin/packages tier at $packages_root (populate via 'make.tcl libfetch') - skipping lib-tier package deployment"
}
} }
if {$::punkboot::command in {bakehouse bake vfslibs}} { if {$::punkboot::command in {bakehouse bake vfslibs}} {
@ -5295,9 +5702,42 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
if {[dict exists $ientry supersedes]} { if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]] set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
} }
set source_pkg_folder $sourcefolder/$isource #source_root (G-139): 'src' (default - the original vendorlib
#declarations) or 'packages' - resolve against the untracked
#bin/packages lib-tier input (populated by 'make.tcl libfetch').
set isource_root "src"
if {[dict exists $ientry source_root]} {
set isource_root [punkboot::lib::toml_untag [dict get $ientry source_root]]
}
#replace (G-139): clean-slate a SAME-NAMED existing target folder
#before install. supersedes deliberately cannot name the folder being
#installed; replace covers same-name refreshes where the old copy may
#carry strays the new tree lacks (e.g. hand-drops nesting md5c/tcllibc
#inside tcllib2.0).
set ireplace 0
if {[dict exists $ientry replace]} {
set ireplace [punkboot::lib::toml_untag [dict get $ientry replace]]
if {![string is boolean -strict $ireplace]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname 'replace' must be boolean (got '$ireplace')$A(RST)"
exit 3
}
}
switch -- $isource_root {
src {
set source_pkg_folder $sourcefolder/$isource
set source_missing_hint ""
}
packages {
set source_pkg_folder $projectroot/bin/packages/$isource
set source_missing_hint " - populate the bin/packages tier first: 'tclsh src/make.tcl libfetch' (declarations in src/runtime/libpackages.toml)"
}
default {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname has unknown source_root '$isource_root' (expected src|packages)$A(RST)"
exit 3
}
}
if {![file isdirectory $source_pkg_folder]} { if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)" puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$source_missing_hint$A(RST)"
exit 3 exit 3
} }
set pkgtail [file tail $source_pkg_folder] set pkgtail [file tail $source_pkg_folder]
@ -5327,6 +5767,10 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
file delete -force $suppath file delete -force $suppath
} }
} }
if {$ireplace && [file isdirectory $target_lib_folder/$pkgtail]} {
puts stdout "VFSLIBS install.$iname: replace=true - removing existing '$pkgtail' from vfs/$t (clean-slate install)"
file delete -force $target_lib_folder/$pkgtail
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)" puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\ set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\ -installer make.tcl\
@ -5341,6 +5785,7 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
} }
} }
if {$::punkboot::command in {bakehouse packages libs}} { if {$::punkboot::command in {bakehouse packages libs}} {
######################################################## ########################################################
lappend projectlibfolders lib lappend projectlibfolders lib

455
src/project_layouts/vendor/punk/basic/src/make.tcl vendored

@ -187,7 +187,7 @@ namespace eval ::punkboot {
variable foldername [file tail $scriptfolder] variable foldername [file tail $scriptfolder]
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list] variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable help_flags [list -help --help /? -h] variable help_flags [list -help --help /? -h]
variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion workflow buildsuite tool project vfs] variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate libfetch bootsupport vfscommonupdate projectversion workflow buildsuite tool project vfs]
} }
@ -2135,6 +2135,10 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n
append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n
append h " - also runs automatically as a phase of '$scriptname bake' (and therefore of '$scriptname bakehouse')" \n \n append h " - also runs automatically as a phase of '$scriptname bake' (and therefore of '$scriptname bakehouse')" \n \n
append h " $scriptname libfetch ?-serverurl <url>? ?-trust-server? ?-force?" \n
append h " - fetch declared punkbin lib-tier library artifacts (src/runtime/libpackages.toml) into bin/packages/<target>" \n
append h " - sha1-verified against the server's per-target sha1sums.txt; materializes package trees under bin/packages/<target>/tcl<N>" \n
append h " - canonical origin trusted by default; other servers (incl. PUNKBIN_URL overrides) require -trust-server" \n \n
append h " $scriptname vfscommonupdate" \n append h " $scriptname vfscommonupdate" \n
append h " - promotion gate: update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n append h " - promotion gate: update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n
append h " - before calling this (followed by '$scriptname bake') - you can test using '<builtexe>(.exe) dev'" \n append h " - before calling this (followed by '$scriptname bake') - you can test using '<builtexe>(.exe) dev'" \n
@ -2323,6 +2327,26 @@ DIAGRAM 2 - KIT ASSEMBLY DETAIL (the 'make.tcl bake' stage; incl. vfslibs phase)
running shell still holds the old exe [K5] running shell still holds the old exe [K5]
LIBRARY-ARTIFACT FETCH (G-139; input tier so far - lib_tcl<N>/kit consumption follows)
----------------------------------------------------------------------------
src/runtime/libpackages.toml which punkbin LIB-TIER artifacts this checkout
consumes (declared artifact names + targets)
|
| 'make.tcl libfetch' - sha1-verified download of the declared
| <pkg>-<tcl8|tcl9>-r<N>.zip artifacts + .toml sidecars from
| <origin>/lib/<target>/ (canonical punkbin origin trusted by
| default; -serverurl/PUNKBIN_URL override needs -trust-server;
| file:// origins serve local mirrors/fixtures), then
| materialization of each verified zip's package tree
v
bin/packages/<target>/ verified -r<N> zips + toml sidecars (input tier,
untracked - mirrors bin/runtime/<target>)
bin/packages/<target>/tcl<N>/<pkgfolder>/
materialized installed-shape trees, self-describing
via their embedded punkbin-artifact.toml records
KEY / NOTES KEY / NOTES
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
[K1] Source modules carry the literal magic version 999999.0a1.0 in the filename. [K1] Source modules carry the literal magic version 999999.0a1.0 in the filename.
@ -2527,6 +2551,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees"
bin "Install executables from src/bin into <projectdir>/bin, then build kits as for bake" bin "Install executables from src/bin into <projectdir>/bin, then build kits as for bake"
vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config" vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config"
libfetch "Fetch declared punkbin lib-tier library artifacts into bin/packages/<target> (sha1-verified) and materialize the package trees"
bootsupport "Promotion gate: update src/bootsupport modules from built project modules" bootsupport "Promotion gate: update src/bootsupport modules from built project modules"
vfscommonupdate "Promotion gate: replace src/vfs/_vfscommon.vfs modules and libs with the project's built set" vfscommonupdate "Promotion gate: replace src/vfs/_vfscommon.vfs modules and libs with the project's built set"
info "Show the name and base folder of the project to be built" info "Show the name and base folder of the project to be built"
@ -2630,6 +2655,24 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
Also runs automatically as a phase of 'make.tcl bake' (and therefore Also runs automatically as a phase of 'make.tcl bake' (and therefore
of 'make.tcl bakehouse'); this standalone narrowing serves quick of 'make.tcl bakehouse'); this standalone narrowing serves quick
iteration on the declarations." iteration on the declarations."
libfetch
"
Fetch the punkbin LIB-TIER library artifacts declared in
src/runtime/libpackages.toml into the untracked input tier
bin/packages/<target>/ - immutable <pkg>-<tcl8|tcl9>-r<N>.zip
artifacts plus their .toml sidecars, each verified against the
server's per-target sha1sums.txt - then materialize each verified
zip's installed-shape package tree under
bin/packages/<target>/tcl<N>/ (self-describing via the embedded
punkbin-artifact.toml record; G-138/G-139).
Present artifacts that already verify are skipped (-force
re-downloads). The canonical punkbin origin is trusted by
default; -serverurl (or env PUNKBIN_URL) overrides it, and any
NON-canonical origin requires the explicit -trust-server flag
(never an interactive prompt - unattended runs stay unattended).
A file:// origin is supported for local mirrors/fixtures.
Transport for http(s): Tcl http+tls when loadable, else curl,
else PowerShell (windows)."
bin bin
" "
Install executables from src/bin to <projectdir>/bin, then check vfs Install executables from src/bin to <projectdir>/bin, then check vfs
@ -2799,6 +2842,30 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
(packages -> test -> bootsupport/vfscommonupdate -> commit -> bake). (packages -> test -> bootsupport/vfscommonupdate -> commit -> bake).
Pass -dirty-abort 0 to build anyway with PROVENANCE-WARNINGs." Pass -dirty-abort 0 to build anyway with PROVENANCE-WARNINGs."
} }
#libfetch fragments (G-139): consent is keyed to SERVER TRUST (the G-123
#posture) - the canonical punkbin origin needs no acknowledgement, any
#other origin needs the explicit flag. Never an interactive prompt.
variable OPT_SERVERURL {
-serverurl -type string -default "" -help\
"Artifact server base url override (default: the canonical punkbin
origin; env PUNKBIN_URL overrides the default, this flag overrides
both). The lib tier is addressed as <serverurl>/lib/<target>/.
file:// urls are supported for local mirrors and fixtures.
A NON-canonical origin additionally requires -trust-server."
}
variable OPT_TRUSTSERVER {
-trust-server -type none -default 0 -help\
"Acknowledge fetching from a NON-canonical artifact server
(consent keyed to server trust - the interim G-123/G-006
mechanism). Without this flag, a non-canonical -serverurl or
PUNKBIN_URL aborts before any network access. Fetches from the
canonical origin never require it."
}
variable OPT_FORCE {
-force -type none -default 0 -help\
"Re-download and re-materialize even when a present artifact
already verifies against the server sha1sums."
}
#which shared option fragments each subcommand declares #which shared option fragments each subcommand declares
#(shell and help have bespoke definitions below: shell passes trailing args #(shell and help have bespoke definitions below: shell passes trailing args
# through to the repl untouched, help takes a subject value) # through to the repl untouched, help takes a subject value)
@ -2812,6 +2879,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
vfslibs {OPT_DIRTYABORT OPT_CONFIRM} vfslibs {OPT_DIRTYABORT OPT_CONFIRM}
bin {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM} bin {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM}
vendorupdate {OPT_CONFIRM} vendorupdate {OPT_CONFIRM}
libfetch {OPT_SERVERURL OPT_TRUSTSERVER OPT_FORCE}
bootsupport {OPT_DIRTYABORT OPT_CONFIRM} bootsupport {OPT_DIRTYABORT OPT_CONFIRM}
vfscommonupdate {OPT_DIRTYABORT OPT_CONFIRM} vfscommonupdate {OPT_DIRTYABORT OPT_CONFIRM}
info {OPT_CONFIRM} info {OPT_CONFIRM}
@ -2829,6 +2897,9 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
OPT_DIRTYABORT "?-dirty-abort?" OPT_DIRTYABORT "?-dirty-abort?"
OPT_DIRTYABORT_ON "?-dirty-abort 1|0?" OPT_DIRTYABORT_ON "?-dirty-abort 1|0?"
OPT_CONFIRM "?-confirm 0|1?" OPT_CONFIRM "?-confirm 0|1?"
OPT_SERVERURL "?-serverurl <url>?"
OPT_TRUSTSERVER "?-trust-server?"
OPT_FORCE "?-force?"
} }
#per-subcommand positional values (default: none). bake (and its deprecated #per-subcommand positional values (default: none). bake (and its deprecated
#alias vfs) and bakelist take optional kit names (G-121 selective bake). #alias vfs) and bakelist take optional kit names (G-121 selective bake).
@ -2940,7 +3011,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
variable SUBGROUPS { variable SUBGROUPS {
"build & bake" {bakehouse packages modules libs bake vfslibs bin} "build & bake" {bakehouse packages modules libs bake vfslibs bin}
"promotion gates" {bootsupport vfscommonupdate} "promotion gates" {bootsupport vfscommonupdate}
"source maintenance" {vendorupdate} "source maintenance" {vendorupdate libfetch}
"buildsuites" {buildsuite} "buildsuites" {buildsuite}
"vendored tools" {tool} "vendored tools" {tool}
"informational" {info check bakelist projectversion workflow help} "informational" {info check bakelist projectversion workflow help}
@ -2996,6 +3067,9 @@ set help_exitcode 0
set ::punkboot::opt_forcekill 0 set ::punkboot::opt_forcekill 0
set ::punkboot::opt_dirty_abort 0 set ::punkboot::opt_dirty_abort 0
set ::punkboot::opt_confirm 1 set ::punkboot::opt_confirm 1
set ::punkboot::opt_serverurl ""
set ::punkboot::opt_trust_server 0
set ::punkboot::opt_force 0
set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bakelist (empty = all configured) set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bakelist (empty = all configured)
if {$::punkboot::punkargs_ok} { if {$::punkboot::punkargs_ok} {
@ -3046,7 +3120,7 @@ if {$::punkboot::punkargs_ok} {
} }
set ::punkboot::command $subcommand set ::punkboot::command $subcommand
set _opts [dict get $argd opts] set _opts [dict get $argd opts]
foreach {_optname _var} {-k opt_forcekill -dirty-abort opt_dirty_abort -confirm opt_confirm} { foreach {_optname _var} {-k opt_forcekill -dirty-abort opt_dirty_abort -confirm opt_confirm -serverurl opt_serverurl -trust-server opt_trust_server -force opt_force} {
if {[dict exists $_opts $_optname]} { if {[dict exists $_opts $_optname]} {
set ::punkboot::$_var [dict get $_opts $_optname] set ::punkboot::$_var [dict get $_opts $_optname]
} }
@ -3083,6 +3157,12 @@ if {$::punkboot::punkargs_ok} {
switch -- $a { switch -- $a {
-k {set ::punkboot::opt_forcekill 1} -k {set ::punkboot::opt_forcekill 1}
-dirty-abort {set ::punkboot::opt_dirty_abort 1} -dirty-abort {set ::punkboot::opt_dirty_abort 1}
-trust-server {set ::punkboot::opt_trust_server 1}
-force {set ::punkboot::opt_force 1}
-serverurl {
set ::punkboot::opt_serverurl [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end]
}
-confirm { -confirm {
set v [lindex $argsleft 0] set v [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end] set argsleft [lrange $argsleft 1 end]
@ -5121,6 +5201,303 @@ if {$::punkboot::command eq "bootsupport"} {
if {$::punkboot::command eq "libfetch"} {
#G-139 phase 1: fetch the punkbin LIB-TIER artifacts declared in
#src/runtime/libpackages.toml into the untracked bin/packages/<target> input
#tier (mirroring bin/runtime/<target>) with sha1 verification against the
#server's per-target sha1sums.txt, then materialize each verified zip's
#installed-shape package tree under bin/packages/<target>/tcl<N>/ (the tree
#is self-describing via its embedded punkbin-artifact.toml - the G-138
#record). Consumption into lib_tcl<N>/ and kit vfs trees is the remainder
#of G-139 - this entrypoint owns fetch + verify + materialize only.
proc ::punkboot::libfetch_transfer {url outfile} {
#one url -> local file. file:// is served natively (local mirrors,
#fixtures - no network, no external tools). http(s) transports in
#order: Tcl http (+tls for https) when loadable, curl (ubiquitous,
#including stock windows 10+), powershell Invoke-WebRequest (windows).
#Returns the transport used; errors when all fail.
file mkdir [file dirname $outfile]
if {[string match -nocase "file://*" $url]} {
set path [string range $url 7 end]
if {[regexp {^/+([A-Za-z]:/.*)$} $path -> winpath]} {
set path $winpath ;#file:///C:/x -> C:/x
}
if {![file isfile $path]} {
error "file origin has no such file: $path"
}
file copy -force $path $outfile
return "file-copy"
}
set is_https [string match -nocase "https://*" $url]
set tcl_http_ok 0
if {![catch {package require http}]} {
if {!$is_https} {
set tcl_http_ok 1
} elseif {![catch {package require tls}]} {
catch {http::register https 443 [list ::tls::socket -autoservername true]}
set tcl_http_ok 1
}
}
if {$tcl_http_ok} {
set fd [open $outfile wb]
set ok 0
if {![catch {http::geturl $url -channel $fd -binary 1} tok]} {
if {[http::status $tok] eq "ok" && [http::ncode $tok] == 200} {set ok 1}
http::cleanup $tok
}
close $fd
if {$ok} {return "tcl-http"}
file delete -force $outfile
}
set curl [auto_execok curl]
if {$curl ne ""} {
if {![catch {exec {*}$curl --fail -sSL -o $outfile $url 2>@1} curlerr]} {
return "curl"
}
file delete -force $outfile
}
if {$::tcl_platform(platform) eq "windows"} {
set ps [auto_execok powershell]
if {$ps ne ""} {
set pscmd "\$ProgressPreference='SilentlyContinue'; Invoke-WebRequest -Uri '$url' -OutFile '$outfile' -ErrorAction Stop"
if {![catch {exec {*}$ps -NoProfile -NonInteractive -Command $pscmd 2>@1} pserr]} {
return "powershell"
}
file delete -force $outfile
}
}
set detail ""
if {[info exists curlerr]} {set detail " (curl: [string range $curlerr 0 200])"}
error "all transports failed for $url$detail"
}
set libfetch_config $sourcefolder/runtime/libpackages.toml
#Canonical origin: the punkbin raw-file base (the punk-runtime.cmd
#convention; lib tier addressed as <origin>/lib/<target>/...).
#Server-trust consent (G-139, the G-123 posture): the canonical origin
#never gates; ANY other origin - flag or PUNKBIN_URL env - requires the
#explicit -trust-server flag. Never an interactive prompt: unattended
#runs stay unattended, and refusal happens before any network access.
set libfetch_canonical "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
set libfetch_origin $libfetch_canonical
if {[info exists ::env(PUNKBIN_URL)] && $::env(PUNKBIN_URL) ne ""} {
set libfetch_origin $::env(PUNKBIN_URL)
}
if {$::punkboot::opt_serverurl ne ""} {
set libfetch_origin $::punkboot::opt_serverurl
}
set libfetch_origin [string trimright $libfetch_origin /]
if {$libfetch_origin ne $libfetch_canonical && !$::punkboot::opt_trust_server} {
puts stderr "$A(BAD)LIBFETCH: origin '$libfetch_origin' is not the canonical punkbin origin$A(RST)"
puts stderr "LIBFETCH: canonical: $libfetch_canonical"
puts stderr "LIBFETCH: fetching from a non-canonical server requires the explicit -trust-server flag (consent keyed to server trust - G-139/G-123). Nothing was fetched."
exit 3
}
if {![file exists $libfetch_config]} {
puts stdout "LIBFETCH: no config at $libfetch_config - nothing declared, nothing to fetch"
} else {
if {[catch {package require tomlish} tomlish_err]} {
puts stderr "$A(BAD)LIBFETCH: cannot process $libfetch_config - tomlish package unavailable ($tomlish_err)$A(RST)"
exit 3
}
if {[catch {package require sha1} sha1_err]} {
puts stderr "$A(BAD)LIBFETCH: sha1 package unavailable ($sha1_err) - cannot verify artifacts$A(RST)"
exit 3
}
if {[catch {package require punk::zip} punkzip_err]} {
puts stderr "$A(BAD)LIBFETCH: punk::zip package unavailable ($punkzip_err) - cannot materialize package trees$A(RST)"
exit 3
}
set fd [open $libfetch_config r]
fconfigure $fd -encoding utf-8
set libfetch_tomldata [read $fd]
close $fd
if {[catch {tomlish::to_dict [tomlish::from_toml $libfetch_tomldata]} libfetch_dict]} {
puts stderr "$A(BAD)LIBFETCH: failed to parse $libfetch_config\n$libfetch_dict$A(RST)"
exit 3
}
if {![llength [info commands ::punkboot::lib::toml_untag]]} {
#tomlish::to_dict values are type-tagged (also defined by the vfslibs
#phase - that block does not run for the libfetch command)
proc ::punkboot::lib::toml_untag {tagged} {
if {[dict get $tagged type] eq "ARRAY"} {
set plain [list]
foreach el [dict get $tagged value] {
lappend plain [::punkboot::lib::toml_untag $el]
}
return $plain
}
return [dict get $tagged value]
}
}
set libfetch_artifacts [dict create]
if {[dict exists $libfetch_dict artifact]} {
set libfetch_artifacts [dict get $libfetch_dict artifact]
}
if {![dict size $libfetch_artifacts]} {
puts stdout "LIBFETCH: no \[artifact.<name>\] entries found in $libfetch_config"
}
puts stdout "LIBFETCH: origin $libfetch_origin ([dict size $libfetch_artifacts] declared artifact(s))"
set libfetch_sums [dict create] ;#target -> dict(filename -> sha1) from the server's sha1sums.txt
set libfetch_failed 0
set libfetch_actions [list]
dict for {aname aentry} $libfetch_artifacts {
foreach reqkey {name target} {
if {![dict exists $aentry $reqkey]} {
puts stderr "$A(BAD)LIBFETCH: entry artifact.$aname in $libfetch_config is missing required key '$reqkey'$A(RST)"
exit 3
}
}
set artifact [punkboot::lib::toml_untag [dict get $aentry name]]
set target [punkboot::lib::toml_untag [dict get $aentry target]]
if {![regexp {^(.+)-(tcl[0-9]+)-r([0-9]+)\.zip$} $artifact -> pkgfolder generation rev]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname name '$artifact' does not match <pkgfolder>-tcl<N>-r<N>.zip$A(RST)"
exit 3
}
set tierdir $projectroot/bin/packages/$target
file mkdir $tierdir
#server truth per target: lib/<target>/sha1sums.txt (fetched once per
#run per target; the local copy in the tier mirrors the punk-runtime
#store idiom)
if {![dict exists $libfetch_sums $target]} {
set sumsurl "$libfetch_origin/lib/$target/sha1sums.txt"
set sumslocal $tierdir/sha1sums.txt
if {[catch {::punkboot::libfetch_transfer $sumsurl $sumslocal} terr]} {
puts stderr "$A(BAD)LIBFETCH: cannot fetch $sumsurl - $terr$A(RST)"
exit 3
}
set sfd [open $sumslocal r]
set sumsdata [read $sfd]
close $sfd
set sums [dict create]
foreach ln [split $sumsdata \n] {
set ln [string trim $ln]
if {$ln eq ""} {continue}
set sp [string first " " $ln]
if {$sp < 1} {continue}
set h [string range $ln 0 $sp-1]
set tag [string range $ln $sp+1 end]
dict set sums [string range $tag 1 end] [string tolower $h]
}
dict set libfetch_sums $target $sums
}
set sums [dict get $libfetch_sums $target]
if {![dict exists $sums $artifact]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: '$artifact' is not listed in the server's lib/$target/sha1sums.txt$A(RST)"
set libfetch_failed 1
continue
}
set want_sha1 [dict get $sums $artifact]
set ziplocal $tierdir/$artifact
set have_ok 0
if {[file isfile $ziplocal] && !$::punkboot::opt_force} {
if {[string tolower [sha1::sha1 -hex -file $ziplocal]] eq $want_sha1} {
set have_ok 1
}
}
if {!$have_ok} {
set zipurl "$libfetch_origin/lib/$target/$artifact"
set partfile $ziplocal.part
if {[catch {::punkboot::libfetch_transfer $zipurl $partfile} terr]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: fetch failed - $terr$A(RST)"
set libfetch_failed 1
continue
}
set got_sha1 [string tolower [sha1::sha1 -hex -file $partfile]]
if {$got_sha1 ne $want_sha1} {
file delete -force $partfile
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sha1 MISMATCH for $artifact (got $got_sha1, server sha1sums says $want_sha1) - deleted$A(RST)"
set libfetch_failed 1
continue
}
file rename -force $partfile $ziplocal
lappend libfetch_actions "fetched lib/$target/$artifact"
} else {
lappend libfetch_actions "verified-present lib/$target/$artifact"
}
#beside-toml sidecar (also sha1sums-listed by emission)
set sidecar "[string range $artifact 0 end-4].toml"
set sidecarlocal $tierdir/$sidecar
set sidecar_ok 0
if {[dict exists $sums $sidecar]} {
set want_side [dict get $sums $sidecar]
if {[file isfile $sidecarlocal] && !$::punkboot::opt_force} {
if {[string tolower [sha1::sha1 -hex -file $sidecarlocal]] eq $want_side} {
set sidecar_ok 1
}
}
if {!$sidecar_ok} {
if {[catch {::punkboot::libfetch_transfer "$libfetch_origin/lib/$target/$sidecar" $sidecarlocal.part} terr]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sidecar fetch failed - $terr$A(RST)"
set libfetch_failed 1
continue
}
if {[string tolower [sha1::sha1 -hex -file $sidecarlocal.part]] ne $want_side} {
file delete -force $sidecarlocal.part
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sidecar sha1 MISMATCH for $sidecar - deleted$A(RST)"
set libfetch_failed 1
continue
}
file rename -force $sidecarlocal.part $sidecarlocal
}
} else {
puts stderr "LIBFETCH: note - $sidecar not listed in server sha1sums (artifact accepted; sidecar skipped)"
}
#materialize: installed-shape tree under <target>/tcl<N>/<pkgfolder>
#(generation subdir because both generations install identically-named
#folders). Fresh tree per materialization; -mtime restores member
#mtimes so the tree mirrors what was zipped.
set gendir $tierdir/$generation
file mkdir $gendir
set treedir $gendir/$pkgfolder
if {[file isdirectory $treedir] && !$::punkboot::opt_force} {
#re-materialize only when the tree's embedded record no longer
#matches this artifact name (self-describing tier - G-138)
set embrec $treedir/punkbin-artifact.toml
set tree_current 0
if {[file isfile $embrec]} {
set efd [open $embrec r]
set edata [read $efd]
close $efd
if {[regexp -line "^name = \"$artifact\"$" $edata]} {
set tree_current 1
}
}
if {$tree_current} {
lappend libfetch_actions "tree-current $target/$generation/$pkgfolder"
continue
}
}
file delete -force $treedir
#defaults: whole archive, -overwrite 1 -mtime 1 (member mtimes restored)
#-verify 1 (per-member crc32) - and accelerator-eligible (punkzip exe)
if {[catch {punk::zip::unzip $ziplocal $gendir} unzip_err]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: materialization failed - $unzip_err$A(RST)"
file delete -force $treedir
set libfetch_failed 1
continue
}
if {![file isfile $treedir/pkgIndex.tcl] || ![file isfile $treedir/punkbin-artifact.toml]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: materialized tree at $treedir lacks pkgIndex.tcl and/or punkbin-artifact.toml$A(RST)"
set libfetch_failed 1
continue
}
lappend libfetch_actions "materialized $target/$generation/$pkgfolder"
}
foreach act $libfetch_actions {
puts stdout "LIBFETCH: $act"
}
if {$libfetch_failed} {
puts stderr "$A(BAD)LIBFETCH: FAILED - one or more declared artifacts could not be fetched/verified/materialized (see above)$A(RST)"
exit 3
}
puts stdout "LIBFETCH: done ([llength $libfetch_actions] action(s)) -> $projectroot/bin/packages"
}
flush stdout
::exit 0
}
if {$::punkboot::command ni {bakehouse packages modules libs bake vfslibs bin}} { if {$::punkboot::command ni {bakehouse packages modules libs bake vfslibs bin}} {
puts stderr "Command $::punkboot::command not implemented - aborting." puts stderr "Command $::punkboot::command not implemented - aborting."
flush stderr flush stderr
@ -5241,6 +5618,36 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)" puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)"
} }
#G-139: punkbin lib-tier packages from the bin/packages input tier (populated
#by 'make.tcl libfetch') into the deployed lib_tcl<N> trees - allplatforms +
#this host's platform dir, mirroring the vendorlib_tcl<N> shape above. The
#materialized trees are self-describing (embedded punkbin-artifact.toml
#records - G-138) and the records ride along into the deployed trees, and
#from there into kit vfs payloads via the vfslibs declarations. An absent
#tier (or target/generation subdir) is a silent per-item no-op: the tier is
#fetch-regenerable and optional until artifacts are declared and fetched.
set packages_root $projectroot/bin/packages
if {[file isdirectory $packages_root]} {
foreach ptarget [list allplatforms $this_platform_generic] {
foreach gendir [lsort [glob -nocomplain -dir $packages_root/$ptarget -type d tcl*]] {
set which [file tail $gendir] ;#tcl8|tcl9 ...
foreach pkgdir [lsort [glob -nocomplain -dir $gendir -type d *]] {
set pkgtail [file tail $pkgdir]
set target_lib_folder $projectroot/lib_$which/$ptarget
file mkdir $target_lib_folder
puts stdout "PACKAGES_$which: copying $ptarget/$which/$pkgtail from the bin/packages tier to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $pkgdir $target_lib_folder/$pkgtail\
-createdir 1\
-overwrite installedsourcechanged-targets\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
} else {
puts stdout "PACKAGES: no bin/packages tier at $packages_root (populate via 'make.tcl libfetch') - skipping lib-tier package deployment"
}
} }
if {$::punkboot::command in {bakehouse bake vfslibs}} { if {$::punkboot::command in {bakehouse bake vfslibs}} {
@ -5295,9 +5702,42 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
if {[dict exists $ientry supersedes]} { if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]] set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
} }
set source_pkg_folder $sourcefolder/$isource #source_root (G-139): 'src' (default - the original vendorlib
#declarations) or 'packages' - resolve against the untracked
#bin/packages lib-tier input (populated by 'make.tcl libfetch').
set isource_root "src"
if {[dict exists $ientry source_root]} {
set isource_root [punkboot::lib::toml_untag [dict get $ientry source_root]]
}
#replace (G-139): clean-slate a SAME-NAMED existing target folder
#before install. supersedes deliberately cannot name the folder being
#installed; replace covers same-name refreshes where the old copy may
#carry strays the new tree lacks (e.g. hand-drops nesting md5c/tcllibc
#inside tcllib2.0).
set ireplace 0
if {[dict exists $ientry replace]} {
set ireplace [punkboot::lib::toml_untag [dict get $ientry replace]]
if {![string is boolean -strict $ireplace]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname 'replace' must be boolean (got '$ireplace')$A(RST)"
exit 3
}
}
switch -- $isource_root {
src {
set source_pkg_folder $sourcefolder/$isource
set source_missing_hint ""
}
packages {
set source_pkg_folder $projectroot/bin/packages/$isource
set source_missing_hint " - populate the bin/packages tier first: 'tclsh src/make.tcl libfetch' (declarations in src/runtime/libpackages.toml)"
}
default {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname has unknown source_root '$isource_root' (expected src|packages)$A(RST)"
exit 3
}
}
if {![file isdirectory $source_pkg_folder]} { if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)" puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$source_missing_hint$A(RST)"
exit 3 exit 3
} }
set pkgtail [file tail $source_pkg_folder] set pkgtail [file tail $source_pkg_folder]
@ -5327,6 +5767,10 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
file delete -force $suppath file delete -force $suppath
} }
} }
if {$ireplace && [file isdirectory $target_lib_folder/$pkgtail]} {
puts stdout "VFSLIBS install.$iname: replace=true - removing existing '$pkgtail' from vfs/$t (clean-slate install)"
file delete -force $target_lib_folder/$pkgtail
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)" puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\ set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\ -installer make.tcl\
@ -5341,6 +5785,7 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
} }
} }
if {$::punkboot::command in {bakehouse packages libs}} { if {$::punkboot::command in {bakehouse packages libs}} {
######################################################## ########################################################
lappend projectlibfolders lib lappend projectlibfolders lib

455
src/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

@ -187,7 +187,7 @@ namespace eval ::punkboot {
variable foldername [file tail $scriptfolder] variable foldername [file tail $scriptfolder]
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list] variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable help_flags [list -help --help /? -h] variable help_flags [list -help --help /? -h]
variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion workflow buildsuite tool project vfs] variable known_commands [list bakehouse packages modules libs bake bakelist vfslibs bin info check shell vendorupdate libfetch bootsupport vfscommonupdate projectversion workflow buildsuite tool project vfs]
} }
@ -2135,6 +2135,10 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n
append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n
append h " - also runs automatically as a phase of '$scriptname bake' (and therefore of '$scriptname bakehouse')" \n \n append h " - also runs automatically as a phase of '$scriptname bake' (and therefore of '$scriptname bakehouse')" \n \n
append h " $scriptname libfetch ?-serverurl <url>? ?-trust-server? ?-force?" \n
append h " - fetch declared punkbin lib-tier library artifacts (src/runtime/libpackages.toml) into bin/packages/<target>" \n
append h " - sha1-verified against the server's per-target sha1sums.txt; materializes package trees under bin/packages/<target>/tcl<N>" \n
append h " - canonical origin trusted by default; other servers (incl. PUNKBIN_URL overrides) require -trust-server" \n \n
append h " $scriptname vfscommonupdate" \n append h " $scriptname vfscommonupdate" \n
append h " - promotion gate: update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n append h " - promotion gate: update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n
append h " - before calling this (followed by '$scriptname bake') - you can test using '<builtexe>(.exe) dev'" \n append h " - before calling this (followed by '$scriptname bake') - you can test using '<builtexe>(.exe) dev'" \n
@ -2323,6 +2327,26 @@ DIAGRAM 2 - KIT ASSEMBLY DETAIL (the 'make.tcl bake' stage; incl. vfslibs phase)
running shell still holds the old exe [K5] running shell still holds the old exe [K5]
LIBRARY-ARTIFACT FETCH (G-139; input tier so far - lib_tcl<N>/kit consumption follows)
----------------------------------------------------------------------------
src/runtime/libpackages.toml which punkbin LIB-TIER artifacts this checkout
consumes (declared artifact names + targets)
|
| 'make.tcl libfetch' - sha1-verified download of the declared
| <pkg>-<tcl8|tcl9>-r<N>.zip artifacts + .toml sidecars from
| <origin>/lib/<target>/ (canonical punkbin origin trusted by
| default; -serverurl/PUNKBIN_URL override needs -trust-server;
| file:// origins serve local mirrors/fixtures), then
| materialization of each verified zip's package tree
v
bin/packages/<target>/ verified -r<N> zips + toml sidecars (input tier,
untracked - mirrors bin/runtime/<target>)
bin/packages/<target>/tcl<N>/<pkgfolder>/
materialized installed-shape trees, self-describing
via their embedded punkbin-artifact.toml records
KEY / NOTES KEY / NOTES
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
[K1] Source modules carry the literal magic version 999999.0a1.0 in the filename. [K1] Source modules carry the literal magic version 999999.0a1.0 in the filename.
@ -2527,6 +2551,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees"
bin "Install executables from src/bin into <projectdir>/bin, then build kits as for bake" bin "Install executables from src/bin into <projectdir>/bin, then build kits as for bake"
vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config" vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config"
libfetch "Fetch declared punkbin lib-tier library artifacts into bin/packages/<target> (sha1-verified) and materialize the package trees"
bootsupport "Promotion gate: update src/bootsupport modules from built project modules" bootsupport "Promotion gate: update src/bootsupport modules from built project modules"
vfscommonupdate "Promotion gate: replace src/vfs/_vfscommon.vfs modules and libs with the project's built set" vfscommonupdate "Promotion gate: replace src/vfs/_vfscommon.vfs modules and libs with the project's built set"
info "Show the name and base folder of the project to be built" info "Show the name and base folder of the project to be built"
@ -2630,6 +2655,24 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
Also runs automatically as a phase of 'make.tcl bake' (and therefore Also runs automatically as a phase of 'make.tcl bake' (and therefore
of 'make.tcl bakehouse'); this standalone narrowing serves quick of 'make.tcl bakehouse'); this standalone narrowing serves quick
iteration on the declarations." iteration on the declarations."
libfetch
"
Fetch the punkbin LIB-TIER library artifacts declared in
src/runtime/libpackages.toml into the untracked input tier
bin/packages/<target>/ - immutable <pkg>-<tcl8|tcl9>-r<N>.zip
artifacts plus their .toml sidecars, each verified against the
server's per-target sha1sums.txt - then materialize each verified
zip's installed-shape package tree under
bin/packages/<target>/tcl<N>/ (self-describing via the embedded
punkbin-artifact.toml record; G-138/G-139).
Present artifacts that already verify are skipped (-force
re-downloads). The canonical punkbin origin is trusted by
default; -serverurl (or env PUNKBIN_URL) overrides it, and any
NON-canonical origin requires the explicit -trust-server flag
(never an interactive prompt - unattended runs stay unattended).
A file:// origin is supported for local mirrors/fixtures.
Transport for http(s): Tcl http+tls when loadable, else curl,
else PowerShell (windows)."
bin bin
" "
Install executables from src/bin to <projectdir>/bin, then check vfs Install executables from src/bin to <projectdir>/bin, then check vfs
@ -2799,6 +2842,30 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
(packages -> test -> bootsupport/vfscommonupdate -> commit -> bake). (packages -> test -> bootsupport/vfscommonupdate -> commit -> bake).
Pass -dirty-abort 0 to build anyway with PROVENANCE-WARNINGs." Pass -dirty-abort 0 to build anyway with PROVENANCE-WARNINGs."
} }
#libfetch fragments (G-139): consent is keyed to SERVER TRUST (the G-123
#posture) - the canonical punkbin origin needs no acknowledgement, any
#other origin needs the explicit flag. Never an interactive prompt.
variable OPT_SERVERURL {
-serverurl -type string -default "" -help\
"Artifact server base url override (default: the canonical punkbin
origin; env PUNKBIN_URL overrides the default, this flag overrides
both). The lib tier is addressed as <serverurl>/lib/<target>/.
file:// urls are supported for local mirrors and fixtures.
A NON-canonical origin additionally requires -trust-server."
}
variable OPT_TRUSTSERVER {
-trust-server -type none -default 0 -help\
"Acknowledge fetching from a NON-canonical artifact server
(consent keyed to server trust - the interim G-123/G-006
mechanism). Without this flag, a non-canonical -serverurl or
PUNKBIN_URL aborts before any network access. Fetches from the
canonical origin never require it."
}
variable OPT_FORCE {
-force -type none -default 0 -help\
"Re-download and re-materialize even when a present artifact
already verifies against the server sha1sums."
}
#which shared option fragments each subcommand declares #which shared option fragments each subcommand declares
#(shell and help have bespoke definitions below: shell passes trailing args #(shell and help have bespoke definitions below: shell passes trailing args
# through to the repl untouched, help takes a subject value) # through to the repl untouched, help takes a subject value)
@ -2812,6 +2879,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
vfslibs {OPT_DIRTYABORT OPT_CONFIRM} vfslibs {OPT_DIRTYABORT OPT_CONFIRM}
bin {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM} bin {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM}
vendorupdate {OPT_CONFIRM} vendorupdate {OPT_CONFIRM}
libfetch {OPT_SERVERURL OPT_TRUSTSERVER OPT_FORCE}
bootsupport {OPT_DIRTYABORT OPT_CONFIRM} bootsupport {OPT_DIRTYABORT OPT_CONFIRM}
vfscommonupdate {OPT_DIRTYABORT OPT_CONFIRM} vfscommonupdate {OPT_DIRTYABORT OPT_CONFIRM}
info {OPT_CONFIRM} info {OPT_CONFIRM}
@ -2829,6 +2897,9 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
OPT_DIRTYABORT "?-dirty-abort?" OPT_DIRTYABORT "?-dirty-abort?"
OPT_DIRTYABORT_ON "?-dirty-abort 1|0?" OPT_DIRTYABORT_ON "?-dirty-abort 1|0?"
OPT_CONFIRM "?-confirm 0|1?" OPT_CONFIRM "?-confirm 0|1?"
OPT_SERVERURL "?-serverurl <url>?"
OPT_TRUSTSERVER "?-trust-server?"
OPT_FORCE "?-force?"
} }
#per-subcommand positional values (default: none). bake (and its deprecated #per-subcommand positional values (default: none). bake (and its deprecated
#alias vfs) and bakelist take optional kit names (G-121 selective bake). #alias vfs) and bakelist take optional kit names (G-121 selective bake).
@ -2940,7 +3011,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
variable SUBGROUPS { variable SUBGROUPS {
"build & bake" {bakehouse packages modules libs bake vfslibs bin} "build & bake" {bakehouse packages modules libs bake vfslibs bin}
"promotion gates" {bootsupport vfscommonupdate} "promotion gates" {bootsupport vfscommonupdate}
"source maintenance" {vendorupdate} "source maintenance" {vendorupdate libfetch}
"buildsuites" {buildsuite} "buildsuites" {buildsuite}
"vendored tools" {tool} "vendored tools" {tool}
"informational" {info check bakelist projectversion workflow help} "informational" {info check bakelist projectversion workflow help}
@ -2996,6 +3067,9 @@ set help_exitcode 0
set ::punkboot::opt_forcekill 0 set ::punkboot::opt_forcekill 0
set ::punkboot::opt_dirty_abort 0 set ::punkboot::opt_dirty_abort 0
set ::punkboot::opt_confirm 1 set ::punkboot::opt_confirm 1
set ::punkboot::opt_serverurl ""
set ::punkboot::opt_trust_server 0
set ::punkboot::opt_force 0
set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bakelist (empty = all configured) set ::punkboot::opt_kitnames [list] ;#G-121: requested kit names for bake/bakelist (empty = all configured)
if {$::punkboot::punkargs_ok} { if {$::punkboot::punkargs_ok} {
@ -3046,7 +3120,7 @@ if {$::punkboot::punkargs_ok} {
} }
set ::punkboot::command $subcommand set ::punkboot::command $subcommand
set _opts [dict get $argd opts] set _opts [dict get $argd opts]
foreach {_optname _var} {-k opt_forcekill -dirty-abort opt_dirty_abort -confirm opt_confirm} { foreach {_optname _var} {-k opt_forcekill -dirty-abort opt_dirty_abort -confirm opt_confirm -serverurl opt_serverurl -trust-server opt_trust_server -force opt_force} {
if {[dict exists $_opts $_optname]} { if {[dict exists $_opts $_optname]} {
set ::punkboot::$_var [dict get $_opts $_optname] set ::punkboot::$_var [dict get $_opts $_optname]
} }
@ -3083,6 +3157,12 @@ if {$::punkboot::punkargs_ok} {
switch -- $a { switch -- $a {
-k {set ::punkboot::opt_forcekill 1} -k {set ::punkboot::opt_forcekill 1}
-dirty-abort {set ::punkboot::opt_dirty_abort 1} -dirty-abort {set ::punkboot::opt_dirty_abort 1}
-trust-server {set ::punkboot::opt_trust_server 1}
-force {set ::punkboot::opt_force 1}
-serverurl {
set ::punkboot::opt_serverurl [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end]
}
-confirm { -confirm {
set v [lindex $argsleft 0] set v [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end] set argsleft [lrange $argsleft 1 end]
@ -5121,6 +5201,303 @@ if {$::punkboot::command eq "bootsupport"} {
if {$::punkboot::command eq "libfetch"} {
#G-139 phase 1: fetch the punkbin LIB-TIER artifacts declared in
#src/runtime/libpackages.toml into the untracked bin/packages/<target> input
#tier (mirroring bin/runtime/<target>) with sha1 verification against the
#server's per-target sha1sums.txt, then materialize each verified zip's
#installed-shape package tree under bin/packages/<target>/tcl<N>/ (the tree
#is self-describing via its embedded punkbin-artifact.toml - the G-138
#record). Consumption into lib_tcl<N>/ and kit vfs trees is the remainder
#of G-139 - this entrypoint owns fetch + verify + materialize only.
proc ::punkboot::libfetch_transfer {url outfile} {
#one url -> local file. file:// is served natively (local mirrors,
#fixtures - no network, no external tools). http(s) transports in
#order: Tcl http (+tls for https) when loadable, curl (ubiquitous,
#including stock windows 10+), powershell Invoke-WebRequest (windows).
#Returns the transport used; errors when all fail.
file mkdir [file dirname $outfile]
if {[string match -nocase "file://*" $url]} {
set path [string range $url 7 end]
if {[regexp {^/+([A-Za-z]:/.*)$} $path -> winpath]} {
set path $winpath ;#file:///C:/x -> C:/x
}
if {![file isfile $path]} {
error "file origin has no such file: $path"
}
file copy -force $path $outfile
return "file-copy"
}
set is_https [string match -nocase "https://*" $url]
set tcl_http_ok 0
if {![catch {package require http}]} {
if {!$is_https} {
set tcl_http_ok 1
} elseif {![catch {package require tls}]} {
catch {http::register https 443 [list ::tls::socket -autoservername true]}
set tcl_http_ok 1
}
}
if {$tcl_http_ok} {
set fd [open $outfile wb]
set ok 0
if {![catch {http::geturl $url -channel $fd -binary 1} tok]} {
if {[http::status $tok] eq "ok" && [http::ncode $tok] == 200} {set ok 1}
http::cleanup $tok
}
close $fd
if {$ok} {return "tcl-http"}
file delete -force $outfile
}
set curl [auto_execok curl]
if {$curl ne ""} {
if {![catch {exec {*}$curl --fail -sSL -o $outfile $url 2>@1} curlerr]} {
return "curl"
}
file delete -force $outfile
}
if {$::tcl_platform(platform) eq "windows"} {
set ps [auto_execok powershell]
if {$ps ne ""} {
set pscmd "\$ProgressPreference='SilentlyContinue'; Invoke-WebRequest -Uri '$url' -OutFile '$outfile' -ErrorAction Stop"
if {![catch {exec {*}$ps -NoProfile -NonInteractive -Command $pscmd 2>@1} pserr]} {
return "powershell"
}
file delete -force $outfile
}
}
set detail ""
if {[info exists curlerr]} {set detail " (curl: [string range $curlerr 0 200])"}
error "all transports failed for $url$detail"
}
set libfetch_config $sourcefolder/runtime/libpackages.toml
#Canonical origin: the punkbin raw-file base (the punk-runtime.cmd
#convention; lib tier addressed as <origin>/lib/<target>/...).
#Server-trust consent (G-139, the G-123 posture): the canonical origin
#never gates; ANY other origin - flag or PUNKBIN_URL env - requires the
#explicit -trust-server flag. Never an interactive prompt: unattended
#runs stay unattended, and refusal happens before any network access.
set libfetch_canonical "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
set libfetch_origin $libfetch_canonical
if {[info exists ::env(PUNKBIN_URL)] && $::env(PUNKBIN_URL) ne ""} {
set libfetch_origin $::env(PUNKBIN_URL)
}
if {$::punkboot::opt_serverurl ne ""} {
set libfetch_origin $::punkboot::opt_serverurl
}
set libfetch_origin [string trimright $libfetch_origin /]
if {$libfetch_origin ne $libfetch_canonical && !$::punkboot::opt_trust_server} {
puts stderr "$A(BAD)LIBFETCH: origin '$libfetch_origin' is not the canonical punkbin origin$A(RST)"
puts stderr "LIBFETCH: canonical: $libfetch_canonical"
puts stderr "LIBFETCH: fetching from a non-canonical server requires the explicit -trust-server flag (consent keyed to server trust - G-139/G-123). Nothing was fetched."
exit 3
}
if {![file exists $libfetch_config]} {
puts stdout "LIBFETCH: no config at $libfetch_config - nothing declared, nothing to fetch"
} else {
if {[catch {package require tomlish} tomlish_err]} {
puts stderr "$A(BAD)LIBFETCH: cannot process $libfetch_config - tomlish package unavailable ($tomlish_err)$A(RST)"
exit 3
}
if {[catch {package require sha1} sha1_err]} {
puts stderr "$A(BAD)LIBFETCH: sha1 package unavailable ($sha1_err) - cannot verify artifacts$A(RST)"
exit 3
}
if {[catch {package require punk::zip} punkzip_err]} {
puts stderr "$A(BAD)LIBFETCH: punk::zip package unavailable ($punkzip_err) - cannot materialize package trees$A(RST)"
exit 3
}
set fd [open $libfetch_config r]
fconfigure $fd -encoding utf-8
set libfetch_tomldata [read $fd]
close $fd
if {[catch {tomlish::to_dict [tomlish::from_toml $libfetch_tomldata]} libfetch_dict]} {
puts stderr "$A(BAD)LIBFETCH: failed to parse $libfetch_config\n$libfetch_dict$A(RST)"
exit 3
}
if {![llength [info commands ::punkboot::lib::toml_untag]]} {
#tomlish::to_dict values are type-tagged (also defined by the vfslibs
#phase - that block does not run for the libfetch command)
proc ::punkboot::lib::toml_untag {tagged} {
if {[dict get $tagged type] eq "ARRAY"} {
set plain [list]
foreach el [dict get $tagged value] {
lappend plain [::punkboot::lib::toml_untag $el]
}
return $plain
}
return [dict get $tagged value]
}
}
set libfetch_artifacts [dict create]
if {[dict exists $libfetch_dict artifact]} {
set libfetch_artifacts [dict get $libfetch_dict artifact]
}
if {![dict size $libfetch_artifacts]} {
puts stdout "LIBFETCH: no \[artifact.<name>\] entries found in $libfetch_config"
}
puts stdout "LIBFETCH: origin $libfetch_origin ([dict size $libfetch_artifacts] declared artifact(s))"
set libfetch_sums [dict create] ;#target -> dict(filename -> sha1) from the server's sha1sums.txt
set libfetch_failed 0
set libfetch_actions [list]
dict for {aname aentry} $libfetch_artifacts {
foreach reqkey {name target} {
if {![dict exists $aentry $reqkey]} {
puts stderr "$A(BAD)LIBFETCH: entry artifact.$aname in $libfetch_config is missing required key '$reqkey'$A(RST)"
exit 3
}
}
set artifact [punkboot::lib::toml_untag [dict get $aentry name]]
set target [punkboot::lib::toml_untag [dict get $aentry target]]
if {![regexp {^(.+)-(tcl[0-9]+)-r([0-9]+)\.zip$} $artifact -> pkgfolder generation rev]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname name '$artifact' does not match <pkgfolder>-tcl<N>-r<N>.zip$A(RST)"
exit 3
}
set tierdir $projectroot/bin/packages/$target
file mkdir $tierdir
#server truth per target: lib/<target>/sha1sums.txt (fetched once per
#run per target; the local copy in the tier mirrors the punk-runtime
#store idiom)
if {![dict exists $libfetch_sums $target]} {
set sumsurl "$libfetch_origin/lib/$target/sha1sums.txt"
set sumslocal $tierdir/sha1sums.txt
if {[catch {::punkboot::libfetch_transfer $sumsurl $sumslocal} terr]} {
puts stderr "$A(BAD)LIBFETCH: cannot fetch $sumsurl - $terr$A(RST)"
exit 3
}
set sfd [open $sumslocal r]
set sumsdata [read $sfd]
close $sfd
set sums [dict create]
foreach ln [split $sumsdata \n] {
set ln [string trim $ln]
if {$ln eq ""} {continue}
set sp [string first " " $ln]
if {$sp < 1} {continue}
set h [string range $ln 0 $sp-1]
set tag [string range $ln $sp+1 end]
dict set sums [string range $tag 1 end] [string tolower $h]
}
dict set libfetch_sums $target $sums
}
set sums [dict get $libfetch_sums $target]
if {![dict exists $sums $artifact]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: '$artifact' is not listed in the server's lib/$target/sha1sums.txt$A(RST)"
set libfetch_failed 1
continue
}
set want_sha1 [dict get $sums $artifact]
set ziplocal $tierdir/$artifact
set have_ok 0
if {[file isfile $ziplocal] && !$::punkboot::opt_force} {
if {[string tolower [sha1::sha1 -hex -file $ziplocal]] eq $want_sha1} {
set have_ok 1
}
}
if {!$have_ok} {
set zipurl "$libfetch_origin/lib/$target/$artifact"
set partfile $ziplocal.part
if {[catch {::punkboot::libfetch_transfer $zipurl $partfile} terr]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: fetch failed - $terr$A(RST)"
set libfetch_failed 1
continue
}
set got_sha1 [string tolower [sha1::sha1 -hex -file $partfile]]
if {$got_sha1 ne $want_sha1} {
file delete -force $partfile
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sha1 MISMATCH for $artifact (got $got_sha1, server sha1sums says $want_sha1) - deleted$A(RST)"
set libfetch_failed 1
continue
}
file rename -force $partfile $ziplocal
lappend libfetch_actions "fetched lib/$target/$artifact"
} else {
lappend libfetch_actions "verified-present lib/$target/$artifact"
}
#beside-toml sidecar (also sha1sums-listed by emission)
set sidecar "[string range $artifact 0 end-4].toml"
set sidecarlocal $tierdir/$sidecar
set sidecar_ok 0
if {[dict exists $sums $sidecar]} {
set want_side [dict get $sums $sidecar]
if {[file isfile $sidecarlocal] && !$::punkboot::opt_force} {
if {[string tolower [sha1::sha1 -hex -file $sidecarlocal]] eq $want_side} {
set sidecar_ok 1
}
}
if {!$sidecar_ok} {
if {[catch {::punkboot::libfetch_transfer "$libfetch_origin/lib/$target/$sidecar" $sidecarlocal.part} terr]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sidecar fetch failed - $terr$A(RST)"
set libfetch_failed 1
continue
}
if {[string tolower [sha1::sha1 -hex -file $sidecarlocal.part]] ne $want_side} {
file delete -force $sidecarlocal.part
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: sidecar sha1 MISMATCH for $sidecar - deleted$A(RST)"
set libfetch_failed 1
continue
}
file rename -force $sidecarlocal.part $sidecarlocal
}
} else {
puts stderr "LIBFETCH: note - $sidecar not listed in server sha1sums (artifact accepted; sidecar skipped)"
}
#materialize: installed-shape tree under <target>/tcl<N>/<pkgfolder>
#(generation subdir because both generations install identically-named
#folders). Fresh tree per materialization; -mtime restores member
#mtimes so the tree mirrors what was zipped.
set gendir $tierdir/$generation
file mkdir $gendir
set treedir $gendir/$pkgfolder
if {[file isdirectory $treedir] && !$::punkboot::opt_force} {
#re-materialize only when the tree's embedded record no longer
#matches this artifact name (self-describing tier - G-138)
set embrec $treedir/punkbin-artifact.toml
set tree_current 0
if {[file isfile $embrec]} {
set efd [open $embrec r]
set edata [read $efd]
close $efd
if {[regexp -line "^name = \"$artifact\"$" $edata]} {
set tree_current 1
}
}
if {$tree_current} {
lappend libfetch_actions "tree-current $target/$generation/$pkgfolder"
continue
}
}
file delete -force $treedir
#defaults: whole archive, -overwrite 1 -mtime 1 (member mtimes restored)
#-verify 1 (per-member crc32) - and accelerator-eligible (punkzip exe)
if {[catch {punk::zip::unzip $ziplocal $gendir} unzip_err]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: materialization failed - $unzip_err$A(RST)"
file delete -force $treedir
set libfetch_failed 1
continue
}
if {![file isfile $treedir/pkgIndex.tcl] || ![file isfile $treedir/punkbin-artifact.toml]} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname: materialized tree at $treedir lacks pkgIndex.tcl and/or punkbin-artifact.toml$A(RST)"
set libfetch_failed 1
continue
}
lappend libfetch_actions "materialized $target/$generation/$pkgfolder"
}
foreach act $libfetch_actions {
puts stdout "LIBFETCH: $act"
}
if {$libfetch_failed} {
puts stderr "$A(BAD)LIBFETCH: FAILED - one or more declared artifacts could not be fetched/verified/materialized (see above)$A(RST)"
exit 3
}
puts stdout "LIBFETCH: done ([llength $libfetch_actions] action(s)) -> $projectroot/bin/packages"
}
flush stdout
::exit 0
}
if {$::punkboot::command ni {bakehouse packages modules libs bake vfslibs bin}} { if {$::punkboot::command ni {bakehouse packages modules libs bake vfslibs bin}} {
puts stderr "Command $::punkboot::command not implemented - aborting." puts stderr "Command $::punkboot::command not implemented - aborting."
flush stderr flush stderr
@ -5241,6 +5618,36 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)" puts stderr "$A(BAD)VENDORLIB: No src/vendorlib or src/vendorlib_tcl* folder found.$A(RST)"
} }
#G-139: punkbin lib-tier packages from the bin/packages input tier (populated
#by 'make.tcl libfetch') into the deployed lib_tcl<N> trees - allplatforms +
#this host's platform dir, mirroring the vendorlib_tcl<N> shape above. The
#materialized trees are self-describing (embedded punkbin-artifact.toml
#records - G-138) and the records ride along into the deployed trees, and
#from there into kit vfs payloads via the vfslibs declarations. An absent
#tier (or target/generation subdir) is a silent per-item no-op: the tier is
#fetch-regenerable and optional until artifacts are declared and fetched.
set packages_root $projectroot/bin/packages
if {[file isdirectory $packages_root]} {
foreach ptarget [list allplatforms $this_platform_generic] {
foreach gendir [lsort [glob -nocomplain -dir $packages_root/$ptarget -type d tcl*]] {
set which [file tail $gendir] ;#tcl8|tcl9 ...
foreach pkgdir [lsort [glob -nocomplain -dir $gendir -type d *]] {
set pkgtail [file tail $pkgdir]
set target_lib_folder $projectroot/lib_$which/$ptarget
file mkdir $target_lib_folder
puts stdout "PACKAGES_$which: copying $ptarget/$which/$pkgtail from the bin/packages tier to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $pkgdir $target_lib_folder/$pkgtail\
-createdir 1\
-overwrite installedsourcechanged-targets\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
} else {
puts stdout "PACKAGES: no bin/packages tier at $packages_root (populate via 'make.tcl libfetch') - skipping lib-tier package deployment"
}
} }
if {$::punkboot::command in {bakehouse bake vfslibs}} { if {$::punkboot::command in {bakehouse bake vfslibs}} {
@ -5295,9 +5702,42 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
if {[dict exists $ientry supersedes]} { if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]] set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
} }
set source_pkg_folder $sourcefolder/$isource #source_root (G-139): 'src' (default - the original vendorlib
#declarations) or 'packages' - resolve against the untracked
#bin/packages lib-tier input (populated by 'make.tcl libfetch').
set isource_root "src"
if {[dict exists $ientry source_root]} {
set isource_root [punkboot::lib::toml_untag [dict get $ientry source_root]]
}
#replace (G-139): clean-slate a SAME-NAMED existing target folder
#before install. supersedes deliberately cannot name the folder being
#installed; replace covers same-name refreshes where the old copy may
#carry strays the new tree lacks (e.g. hand-drops nesting md5c/tcllibc
#inside tcllib2.0).
set ireplace 0
if {[dict exists $ientry replace]} {
set ireplace [punkboot::lib::toml_untag [dict get $ientry replace]]
if {![string is boolean -strict $ireplace]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname 'replace' must be boolean (got '$ireplace')$A(RST)"
exit 3
}
}
switch -- $isource_root {
src {
set source_pkg_folder $sourcefolder/$isource
set source_missing_hint ""
}
packages {
set source_pkg_folder $projectroot/bin/packages/$isource
set source_missing_hint " - populate the bin/packages tier first: 'tclsh src/make.tcl libfetch' (declarations in src/runtime/libpackages.toml)"
}
default {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname has unknown source_root '$isource_root' (expected src|packages)$A(RST)"
exit 3
}
}
if {![file isdirectory $source_pkg_folder]} { if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)" puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$source_missing_hint$A(RST)"
exit 3 exit 3
} }
set pkgtail [file tail $source_pkg_folder] set pkgtail [file tail $source_pkg_folder]
@ -5327,6 +5767,10 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
file delete -force $suppath file delete -force $suppath
} }
} }
if {$ireplace && [file isdirectory $target_lib_folder/$pkgtail]} {
puts stdout "VFSLIBS install.$iname: replace=true - removing existing '$pkgtail' from vfs/$t (clean-slate install)"
file delete -force $target_lib_folder/$pkgtail
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)" puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\ set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\ -installer make.tcl\
@ -5341,6 +5785,7 @@ if {$::punkboot::command in {bakehouse bake vfslibs}} {
} }
} }
if {$::punkboot::command in {bakehouse packages libs}} { if {$::punkboot::command in {bakehouse packages libs}} {
######################################################## ########################################################
lappend projectlibfolders lib lappend projectlibfolders lib

86
src/runtime/vendorlib_vfs.toml

@ -16,11 +16,20 @@
# by generation - so the same source folder serves lib_tcl9 and lib_tcl8 targets). # by generation - so the same source folder serves lib_tcl9 and lib_tcl8 targets).
# #
# Keys per [install.<name>] entry: # Keys per [install.<name>] entry:
# source - package folder path relative to src/ # source - package folder path, resolved per source_root
# targets - list of kit vfs lib folders relative to src/vfs/ # source_root - optional: 'src' (default; source is relative to src/ - the
# supersedes - optional list of legacy/older package folder names to remove from each # vendored-tree declarations) or 'packages' (G-139; source is
# target before install (explicit - no silent mixed-version provision, per # relative to <projectroot>/bin/packages - the untracked punkbin
# the G-035 concerns) # lib-tier input populated by 'tclsh src/make.tcl libfetch')
# targets - list of kit vfs lib folders relative to src/vfs/
# supersedes - optional list of legacy/older package folder names to remove from
# each target before install (explicit - no silent mixed-version
# provision, per the G-035 concerns). Cannot name the folder being
# installed (same-name entries are ignored) - use 'replace' for that.
# replace - optional boolean (G-139): clean-slate a SAME-NAMED existing target
# folder before install. For same-name refreshes where the old copy
# may carry strays the new tree lacks (the pre-G-139 hand-drops
# nested md5c/tcllibc INSIDE their tcllib2.0 folder).
# #
# This file is a candidate for absorption into the mapvfs toml conversion (goal G-024). # This file is a candidate for absorption into the mapvfs toml conversion (goal G-024).
@ -34,3 +43,70 @@ targets = [
"punk8win.vfs/lib_tcl8", "punk8win.vfs/lib_tcl8",
] ]
supersedes = ["udp1.0.12", "tcludp1.0.12"] supersedes = ["udp1.0.12", "tcludp1.0.12"]
# -- G-139: suite-built tcllib/tcllibc from the punkbin lib tier ---------------
# Sourced from the bin/packages input tier ('make.tcl libfetch'; artifacts
# declared in libpackages.toml). Targets = the kit vfs trees that carried
# hand-dropped copies at switchover (2026-07-30) - per-kit participation, never
# blanket. replace=true because the old tcl9 hand-drops nested md5c + tcllibc
# INSIDE tcllib2.0 (same folder name as the new install - supersedes cannot
# touch it). The tcl8 kits upgrade tcllib1.21 (+nested tcllibc 0.4 + md5c) ->
# tcllib 2.0 via supersedes. tcllibc is the win32-x86_64 build: punk9linux
# (linux target) deliberately gets only the platform-neutral tcllib2.0 - its
# linux-built tcllibc/tcllibc2.0 sibling folders are left as-is (a linux
# lib-tier emission is future work). punk86bawt carried a bare tcllibc only -
# it gets the tcllibc refresh and no tcllib entry.
[install.tcllib_tcl9]
source_root = "packages"
source = "allplatforms/tcl9/tcllib2.0"
replace = true
targets = [
"mkzipfix.vfs/lib_tcl9",
"punk9cook.vfs/lib_tcl9",
"punk9cookbasic.vfs/lib_tcl9",
"punk9linux.vfs/lib_tcl9",
"punk9magicsplat.vfs/lib_tcl9",
"punk9win_for_tkruntime.vfs/lib_tcl9",
"punk9wintk903.vfs/lib_tcl9",
"punk9wintk905.vfs/lib_tcl9",
"punk9wintk90b2.vfs/lib_tcl9",
]
[install.tcllibc_tcl9]
source_root = "packages"
source = "win32-x86_64/tcl9/tcllibc"
replace = true
targets = [
"mkzipfix.vfs/lib_tcl9",
"punk9cook.vfs/lib_tcl9",
"punk9cookbasic.vfs/lib_tcl9",
"punk9magicsplat.vfs/lib_tcl9",
"punk9win_for_tkruntime.vfs/lib_tcl9",
"punk9wintk903.vfs/lib_tcl9",
"punk9wintk905.vfs/lib_tcl9",
"punk9wintk90b2.vfs/lib_tcl9",
]
supersedes = ["tcllibc2.0"]
[install.tcllib_tcl8]
source_root = "packages"
source = "allplatforms/tcl8/tcllib2.0"
replace = true
targets = [
"punk86.vfs/lib_tcl8",
"punk8win.vfs/lib_tcl8",
"punk8_statictwapi.vfs/lib_tcl8",
]
supersedes = ["tcllib1.21"]
[install.tcllibc_tcl8]
source_root = "packages"
source = "win32-x86_64/tcl8/tcllibc"
replace = true
targets = [
"punk86.vfs/lib_tcl8",
"punk8win.vfs/lib_tcl8",
"punk8_statictwapi.vfs/lib_tcl8",
"punk86bawt.vfs/lib_tcl8",
]

Loading…
Cancel
Save