Browse Source

G-104: make.tcl buildsuite list/info/build + sources.config self-description contract

buildsuite group added as a declared passthrough (punk::args + fallback paths):
list discovers suites (dirs carrying suite.tcl; _build excluded), info renders
the manifest-derived detail, build forwards driver args untouched streaming
output and exiting with the driver's status (punk-kit-hosted runs launch the
driver via the kit's script subcommand, selected by the G-058 marker).

Self-description = manifest option: sources.config gains description/product/
doc/zigpin records parsed by both suite.tcl (strict; pinned-zig default now
reads zigpin - one source of truth) and make.tcl (tolerant). suite_tcl90
seeded with its records. Copied trees appear automatically (verified with a
throwaway copy). Contract documented in new src/buildsuites/README.md +
AGENTS.md, indexed from src/AGENTS.md. CHANGELOG extended under 0.23.0.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 5 days ago
parent
commit
889d3cd1c7
  1. 10
      CHANGELOG.md
  2. 27
      goals/G-104-maketcl-buildsuite-surface.md
  3. 1
      src/AGENTS.md
  4. 42
      src/buildsuites/AGENTS.md
  5. 70
      src/buildsuites/README.md
  6. 16
      src/buildsuites/suite_tcl90/sources.config
  7. 90
      src/buildsuites/suite_tcl90/suite.tcl
  8. 209
      src/make.tcl

10
CHANGELOG.md

@ -16,6 +16,16 @@ Entries are newest-first; one bullet per notable change. See the root `AGENTS.md
the 8.6 arc completes). `vfslibs` survives as a standalone narrowing for declaration the 8.6 arc completes). `vfslibs` survives as a standalone narrowing for declaration
iteration. Help/usage tables regrouped (build & bake / promotion gates / source iteration. Help/usage tables regrouped (build & bake / promotion gates / source
maintenance / deprecated aliases); `workflow` output teaches the two-persona model. maintenance / deprecated aliases); `workflow` output teaches the two-persona model.
- make.tcl buildsuite surface (G-104): `buildsuite list` discovers the suites under
src/buildsuites (directories carrying a suite.tcl driver; _build excluded) with
one-line descriptions; `buildsuite info <name>` renders the suite's configured detail
(sources.config records, pinned zig + presence, products, doc pointers); `buildsuite
build <name> ?driver-args?` runs the suite driver forwarding args untouched, streaming
output and exiting with the driver's status. Suites are self-describing via new
sources.config records (description/product/doc/zigpin - same manifest the driver
parses; suite.tcl's pinned-zig default now reads the zigpin record), so copied
retargeted trees appear automatically. Contract documented in src/buildsuites/README.md
(new) + AGENTS.md (new). Works under plain tclsh and punk kit executables.
## [0.22.2] - 2026-07-25 ## [0.22.2] - 2026-07-25

27
goals/G-104-maketcl-buildsuite-surface.md

@ -32,6 +32,33 @@ Relationships: G-096 (achieved 2026-07-20 - suite.tcl driver + copy-and-tweak wo
G-102 (achieved 2026-07-21 - driver shape settled; the info/build contract holds), G-103 (achieved 2026-07-22 - see goals/archive/G-103-runtime-kit-family.md; family G-102 (achieved 2026-07-21 - driver shape settled; the info/build contract holds), G-103 (achieved 2026-07-22 - see goals/archive/G-103-runtime-kit-family.md; family
products appear in info output), G-105 (target dimension forwarded when it exists). products appear in info output), G-105 (target dimension forwarded when it exists).
## Progress
- 2026-07-25 surface landed (punkshell 0.23.0, written directly in the G-112
stage-true vocabulary): make.tcl gains the buildsuite group (list/info/build)
as a declared passthrough beside shell (driver args forwarded untouched in
both the punk::args and degraded-fallback dispatch paths). Self-description
contract settled on the MANIFEST option: sources.config gains
description/product/doc/zigpin records parsed by BOTH suite.tcl (strict; its
pinned-zig default now reads the zigpin record - single source of truth) and
make.tcl (tolerant - a malformed/absent manifest lists with the problem as
its description instead of breaking discovery). Discovery = directory scan
for a suite.tcl driver, _build excluded (samplesuite1 auto-excluded).
Contract documented in src/buildsuites/README.md (new) + AGENTS.md (new,
indexed from src/AGENTS.md); make.tcl help/plain-help document the group.
- Verified 2026-07-25: list shows suite_tcl90 with its sources.config-derived
description; info renders the sources table + zig pin (present) + products +
doc pointers; build forwards args and exits with the driver's status
(unknown-option run exit 1 streamed; unknown suite/action exit 2; list exit
0); works under native tclsh90 AND punk91-kit-hosted (script-mode launcher
selection via the G-058 static-capture marker); a throwaway COPY of
suite_tcl90 with an edited description appeared in list/info automatically
and was removed after the check. suite.tcl direct CLI re-verified
(usage-error path through the new config-load position).
- Remains for acceptance: a real driver run through the surface (satisfied
naturally when the G-099 suite_tcl86 arc drives builds via 'make.tcl
buildsuite build'); window doc sweep.
## Notes ## Notes
- Activation freshness 2026-07-25: goals_xref.tcl score G-104 re-run at the - Activation freshness 2026-07-25: goals_xref.tcl score G-104 re-run at the

1
src/AGENTS.md

@ -116,5 +116,6 @@ Recovery after a wrong path guess:
- `vendorlib_tcl8/` — Tcl 8 specific vendor libraries - `vendorlib_tcl8/` — Tcl 8 specific vendor libraries
- `vendorlib_tcl9/` — Tcl 9 specific vendor libraries - `vendorlib_tcl9/` — Tcl 9 specific vendor libraries
- `runtime/` — Build runtimes and VFS config (see runtime/AGENTS.md) - `runtime/` — Build runtimes and VFS config (see runtime/AGENTS.md)
- `buildsuites/` — Zig-built runtime factory: suites building Tcl runtimes from external sources with the pinned zig toolchain; surfaced via `make.tcl buildsuite list/info/build`; suite self-description contract in buildsuites/README.md (see buildsuites/AGENTS.md)
- `doc/` — Generated documentation (see doc/AGENTS.md) - `doc/` — Generated documentation (see doc/AGENTS.md)
- `testansi/` — Sample ANSI art files (do not modify) - `testansi/` — Sample ANSI art files (do not modify)

42
src/buildsuites/AGENTS.md

@ -0,0 +1,42 @@
# src/buildsuites - Buildsuites (zig runtime factory)
## Purpose
Suites that build Tcl runtimes and binary companions from external upstream
sources using the pinned zig toolchain (zig-only build policy). Arm's-length
from the project build/bake stages: inputs are upstream checkouts/pins, not
this project's `src/` tree.
## Ownership
- Suite trees (`suite_tcl90/`, future `suite_tcl86/` etc) are agent-editable
under user-directed goal work (the G-096/G-099 arcs).
- `_build/` is a VCS-ignored build area - never commit from it; each suite
stages into `_build/<suitename>`.
## Local Contracts
- The suite self-description contract (what makes a directory a suite, the
`sources.config` record grammar incl. description/product/doc/zigpin, the
copy-and-tweak guarantee) is documented in `README.md` here - keep it and
the `make.tcl buildsuite` surface in step.
- `sources.config` is parsed, never executed - by both the suite driver and
`make.tcl buildsuite list/info`. Grammar changes must land in both parsers
(suite.tcl strict, make.tcl tolerant) plus `README.md`.
- Fossil clones live in the machine-level store (`PUNK_FOSSIL_STORE`, default
`~/.punkshell/fossils`) - outside `_build`-class deletable dirs.
- Suite drivers must keep working when invoked directly (`tclsh suite.tcl ...`)
AND via `make.tcl buildsuite build <name>` (which forwards args untouched).
## Verification
- `tclsh src/make.tcl buildsuite list` shows every suite with its description.
- `tclsh src/make.tcl buildsuite info <name>` renders the manifest-derived
detail for the touched suite.
- Suite pipelines verify per their own README/goal records (test gates vs
tracked expected_test_failures baselines).
## Child DOX Index
- `suite_tcl90/` - Tcl 9.0 suite (its README.md carries lineage/detail; no
child AGENTS.md yet)

70
src/buildsuites/README.md

@ -0,0 +1,70 @@
# src/buildsuites - zig-built runtime factory
Buildsuites build Tcl runtimes (and their binary companions) from external
sources with the pinned zig toolchain. This is an arm's-length subsystem: it
consumes upstream Tcl/Tk/extension sources, not this project's `src/` tree,
and its outputs are runtime executables and artifact families - distinct from
the project build/bake stages driven by the rest of `make.tcl`.
## Layout
- `suite_tcl90/` - the Tcl 9.0 suite (see its README for lineage and detail)
- `_build/` - VCS-ignored build area; each suite stages into `_build/<suitename>`
- other directories without a `suite.tcl` driver are not suites (e.g sample
material) and are ignored by discovery
## The make.tcl surface (G-104)
Run from the projectroot:
tclsh src/make.tcl buildsuite list
tclsh src/make.tcl buildsuite info <suitename>
tclsh src/make.tcl buildsuite build <suitename> ?driver-args ...?
`list` discovers the suites and their one-line descriptions. `info` shows a
suite's configured detail: description, source records (url/ref/pin), the
pinned zig and whether it is present, products, and doc pointers. `build`
runs the suite's driver (`suite.tcl build`) forwarding the given args
untouched (e.g `-tclbranch`, `-refresh`, `-steps`), streams its output, and
exits with the driver's status. The surface works under a plain tclsh and
under punk kit executables.
## Suite self-description contract
A directory under `src/buildsuites` IS a suite when it carries a `suite.tcl`
driver. Its `sources.config` is the suite's manifest - parsed as plain line
records, never executed - read by BOTH the driver and `make.tcl buildsuite
list/info`. Record grammar (whitespace-separated; `#` comments and blanks
ignored):
source <name> <kind> <url> <ref> <dir> source-pull declarations (fossil|git)
description <text...> one line - the suite's list/info one-liner
product <text...> repeatable - what a full build produces
doc <relpath> repeatable - suite-relative doc pointers
zigpin <relpath> projectroot-relative pinned zig executable
Because discovery is a directory scan and description/info are answered by the
suite's own manifest, a copied tree (the copy-and-tweak retargeting workflow -
copy `suite_tcl90/` to `src/buildsuites/<yourname>/`, edit `sources.config`)
appears in `list`/`info` automatically and stages into its own
`_build/<yourname>` - no `make.tcl` edits, ever. A new or forked suite
satisfies the contract by construction: keep `suite.tcl` + `sources.config`
and edit the description/product records to tell the truth about your suite.
The `zigpin` record is the single source of truth for the suite's pinned zig:
`suite.tcl` resolves its default zig from it (precedence: `-zig` option >
`PUNK_ZIG` env > the pin > `zig` on PATH) and `info` reports it with presence
state (`bin/punk-getzig.cmd` fetches pinned zig versions).
## Flows
Each suite offers two equivalent flows (G-102):
- FOSSIL DEV FLOW: `suite.tcl build|test|clean` - live checkouts per
`sources.config` refs (branches may float), staged from the hermetic clone
store (`PUNK_FOSSIL_STORE`, default `~/.punkshell/fossils`).
- NO-TCLSH FLOW: `zig build stage|bootstrap` from the suite directory -
content-hashed per-checkin pins from `build.zig.zon`.
Both delegate the pipeline (configure, compile, install, smoke, test gates)
to the suite's zig recipe steps.

16
src/buildsuites/suite_tcl90/sources.config

@ -41,6 +41,22 @@
# cloned directly from <url> and pinned to <ref>. critcl is fetched lazily - # cloned directly from <url> and pinned to <ref>. critcl is fetched lazily -
# only when the tcllibc accelerators need building. # only when the tcllibc accelerators need building.
# -- suite self-description records (G-104) --------------------------------
# Consumed by 'make.tcl buildsuite list/info' AND by suite.tcl (same parser
# grammar both sides - the file stays the single manifest). Part of the
# copy-and-tweak surface: edit these for your copied suite so it lists and
# describes itself with no make.tcl edits.
# description <text...> one line - the list/info one-liner for this suite
# product <text...> repeatable - what a full build produces
# doc <relpath> repeatable - suite-relative doc pointers
# zigpin <relpath> projectroot-relative pinned zig executable
description Zig-built Tcl 9.0 windows runtime family: core + thread/tclvfs/Tk + tcllib/tcllibc, core-test gate, punk kit-family artifacts
product tclsh9.0.x runtime family (plain / punk / punk-bi) with attached prefix-mirror image - out/family plus punkbin-layout -r<N> artifacts with toml metadata
product installed lib tree: thread, tclvfs, Tk, tklib, tcllib (+ tcllibc critcl accelerators)
product core testsuite gate vs the tracked expected_test_failures baseline (parsed all.tcl totals)
doc README.md
zigpin bin/tools/zig-x86_64-windows-0.16.0/zig.exe
source tcl fossil https://core.tcl-lang.org/tcl core-9-0-branch tcl905 source tcl fossil https://core.tcl-lang.org/tcl core-9-0-branch tcl905
source tclthread fossil https://core.tcl-lang.org/thread trunk tclthread source tclthread fossil https://core.tcl-lang.org/thread trunk tclthread
# tclvfs forTcl9 branch (user 2026-07-22): trunk is the 1.4.2 era; forTcl9 carries # tclvfs forTcl9 branch (user 2026-07-22): trunk is the 1.4.2 era; forTcl9 carries

90
src/buildsuites/suite_tcl90/suite.tcl

@ -71,6 +71,62 @@ set suiteroot [file dirname [file normalize [info script]]]
set stage [file normalize [file join $suiteroot .. _build [file tail $suiteroot]]] set stage [file normalize [file join $suiteroot .. _build [file tail $suiteroot]]]
set builddir [file join $stage build] set builddir [file join $stage build]
#-- sources.config load ---------------------------------------------------
#Declared in sources.config (the copy-and-tweak surface for the LIVE-checkout dev
#flow - see its header; build.zig.zon is the equivalent PINNED surface for the
#no-tclsh flow). Parsed as line records - never executed.
#Extended record grammar (G-104 self-description contract - documented in
#src/buildsuites/README.md; 'make.tcl buildsuite list/info' reads the same
#records, so a copied suite self-describes with no make.tcl edits):
# source <name> <kind> <url> <ref> <dir> source-pull declarations (the original records)
# description <text...> one line - the suite's list/info one-liner
# product <text...> repeatable - what the suite produces
# doc <relpath> repeatable - suite-relative doc pointers
# zigpin <relpath> projectroot-relative pinned zig executable
proc load_sources_config {path} {
set config [dict create sources [dict create] description "" products [list] docs [list] zigpin ""]
set f [open $path r]; set data [read $f]; close $f
set ln 0
foreach line [split $data \n] {
incr ln
set line [string trim $line]
if {$line eq "" || [string index $line 0] eq "#"} {continue}
if {[catch {llength $line} len]} {
fail "sources.config line $ln: not a well-formed record: $line"
}
switch -- [lindex $line 0] {
source {
if {$len != 6} {
fail "sources.config line $ln: expected 'source <name> <kind> <url> <ref> <dir>' - got: $line"
}
lassign $line -> name kind url ref dir
if {$kind ni {fossil git}} {
fail "sources.config line $ln: unknown kind '$kind' (expected fossil|git)"
}
dict set config sources $name [dict create kind $kind url $url ref $ref dir $dir]
}
description {
dict set config description [join [lrange $line 1 end] " "]
}
product {
dict lappend config products [join [lrange $line 1 end] " "]
}
doc {
dict lappend config docs [lindex $line 1]
}
zigpin {
dict set config zigpin [lindex $line 1]
}
default {
fail "sources.config line $ln: unknown record type '[lindex $line 0]' (expected source|description|product|doc|zigpin)"
}
}
}
return $config
}
set suiteconfig [load_sources_config [file join $suiteroot sources.config]]
set sources [dict get $suiteconfig sources]
#-- options --------------------------------------------------------------- #-- options ---------------------------------------------------------------
set action [lindex $argv 0] set action [lindex $argv 0]
if {$action ni {build test clean}} { if {$action ni {build test clean}} {
@ -92,8 +148,14 @@ set opt(-seedfossils) [file normalize [file join $env(HOME) .fossils]]
#zig resolution: -zig option > PUNK_ZIG env > the suite's pinned toolchain under #zig resolution: -zig option > PUNK_ZIG env > the suite's pinned toolchain under
#bin/tools (zig 0.16.0 official release - hosted on punkbin, minisign-verified; #bin/tools (zig 0.16.0 official release - hosted on punkbin, minisign-verified;
#the recipe is 0.16-API and does NOT build under 0.14/0.15 - see README Lineage) #the recipe is 0.16-API and does NOT build under 0.14/0.15 - see README Lineage)
#> 'zig' on PATH. #> 'zig' on PATH. The pinned path comes from the sources.config 'zigpin' record
#(projectroot-relative; single source of truth shared with 'make.tcl buildsuite
#info') with the historical suite_tcl90 path as fallback when the record is absent.
if {[dict get $suiteconfig zigpin] ne ""} {
set pinned_zig [file normalize [file join $suiteroot .. .. .. [dict get $suiteconfig zigpin]]]
} else {
set pinned_zig [file normalize [file join $suiteroot .. .. .. bin tools zig-x86_64-windows-0.16.0 zig.exe]] set pinned_zig [file normalize [file join $suiteroot .. .. .. bin tools zig-x86_64-windows-0.16.0 zig.exe]]
}
if {[info exists env(PUNK_ZIG)]} { if {[info exists env(PUNK_ZIG)]} {
set opt(-zig) $env(PUNK_ZIG) set opt(-zig) $env(PUNK_ZIG)
} elseif {[file exists $pinned_zig]} { } elseif {[file exists $pinned_zig]} {
@ -269,29 +331,9 @@ foreach item {build905.zig build_common.zig build_libtommath build_zlib build_tc
} }
#-- sources --------------------------------------------------------------- #-- sources ---------------------------------------------------------------
#Declared in sources.config (the copy-and-tweak surface for the LIVE-checkout dev #(sources.config already loaded near the top - see load_sources_config there;
#flow - see its header; build.zig.zon is the equivalent PINNED surface for the #$sources holds the source records, $suiteconfig the full record set incl. the
#no-tclsh flow). Parsed as line records - never executed. #G-104 self-description records)
proc load_sources_config {path} {
set sources [dict create]
set f [open $path r]; set data [read $f]; close $f
set ln 0
foreach line [split $data \n] {
incr ln
set line [string trim $line]
if {$line eq "" || [string index $line 0] eq "#"} {continue}
if {[catch {llength $line} len] || $len != 6 || [lindex $line 0] ne "source"} {
fail "sources.config line $ln: expected 'source <name> <kind> <url> <ref> <dir>' - got: $line"
}
lassign $line -> name kind url ref dir
if {$kind ni {fossil git}} {
fail "sources.config line $ln: unknown kind '$kind' (expected fossil|git)"
}
dict set sources $name [dict create kind $kind url $url ref $ref dir $dir]
}
return $sources
}
set sources [load_sources_config [file join $suiteroot sources.config]]
foreach required {tcl tclthread tclvfs tk tklib tcllib critcl} { foreach required {tcl tclthread tclvfs tk tklib tcllib critcl} {
if {![dict exists $sources $required]} { if {![dict exists $sources $required]} {
fail "sources.config: missing required source record '$required'" fail "sources.config: missing required source record '$required'"

209
src/make.tcl

@ -153,7 +153,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 vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion workflow project vfs] variable known_commands [list bakehouse packages modules libs bake vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion workflow buildsuite project vfs]
} }
@ -1563,6 +1563,9 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - advisory check: verify CHANGELOG.md matches punkproject.toml and warn if src/ has changes since the last project-version bump." \n \n append h " - advisory check: verify CHANGELOG.md matches punkproject.toml and warn if src/ has changes since the last project-version bump." \n \n
append h " $scriptname workflow" \n append h " $scriptname workflow" \n
append h " - print an ASCII data-flow overview of the build/release workflow (edit -> packages -> bootsupport -> kits)" \n \n append h " - print an ASCII data-flow overview of the build/release workflow (edit -> packages -> bootsupport -> kits)" \n \n
append h " $scriptname buildsuite list|info|build ?<suitename>? ?driver-args ...?" \n
append h " - list the defined buildsuites under src/buildsuites (zig runtime factory), show a suite's configured" \n
append h " detail (sources/zig pin/products from its sources.config), or run its driver forwarding the args" \n \n
append h " $scriptname help ?subcommand?" \n append h " $scriptname help ?subcommand?" \n
append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname <subcommand> -help)" \n \n append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname <subcommand> -help)" \n \n
append h " Deprecated aliases (transition window):" \n append h " Deprecated aliases (transition window):" \n
@ -1858,6 +1861,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
workflow "Print an ASCII data-flow overview of the build/release workflow (edit -> packages -> gates -> bake)" workflow "Print an ASCII data-flow overview of the build/release workflow (edit -> packages -> gates -> bake)"
shell "Run the punk shell using bootsupport libraries" shell "Run the punk shell using bootsupport libraries"
help "Show usage for make.tcl or one of its subcommands" help "Show usage for make.tcl or one of its subcommands"
buildsuite "List, describe or run the defined buildsuites under src/buildsuites (zig runtime factory)"
project "DEPRECATED alias for bakehouse (one-line notice, then runs bakehouse)" project "DEPRECATED alias for bakehouse (one-line notice, then runs bakehouse)"
vfs "DEPRECATED alias for bake (one-line notice, then runs bake)" vfs "DEPRECATED alias for bake (one-line notice, then runs bake)"
} }
@ -1971,6 +1975,31 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
The text is embedded in make.tcl (::punkboot::workflow_text) so it is The text is embedded in make.tcl (::punkboot::workflow_text) so it is
available wherever make.tcl runs; its MAINTENANCE key states the available wherever make.tcl runs; its MAINTENANCE key states the
update contract for keeping it in step with build behaviour." update contract for keeping it in step with build behaviour."
buildsuite
"
Surface for the defined buildsuites under src/buildsuites (zig-built
runtime factory - an arm's-length subsystem building Tcl runtimes
from external sources, distinct from the project build/bake stages).
Actions:
buildsuite list
Discover the suites (directories carrying a suite.tcl driver;
_build excluded) with their one-line descriptions.
buildsuite info <name>
Show the suite's configured detail: description, source records
(name/kind/url/ref/dir from sources.config), pinned zig and its
resolution, products, doc pointers.
buildsuite build <name> ?driver-args ...?
Run the suite's driver ('suite.tcl build' with the given args
forwarded untouched, e.g -tclbranch/-refresh/-steps), streaming
its output and exiting with the driver's status.
Suites are SELF-describing: list/info read each suite's
sources.config records (description/product/doc/zigpin - the same
manifest the suite.tcl driver parses), so a copied/retargeted tree
(copy-and-tweak workflow) appears here automatically with no
make.tcl edits. The contract is documented in
src/buildsuites/README.md."
project project
" "
DEPRECATED alias for 'bakehouse' (G-112 stage-true rename) - prints a DEPRECATED alias for 'bakehouse' (G-112 stage-true rename) - prints a
@ -2084,6 +2113,15 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
{@values -min 0 -max -1 {@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\ arg -type any -optional 1 -multiple 1 -help\
"Arguments passed through to the punk repl."} "Arguments passed through to the punk repl."}
punk::args::define\
"@id -id (script)::punkboot::buildsuite"\
"@normalize"\
"@cmd -name [list "make.tcl buildsuite"] -summary [list [dict get $SUMMARIES buildsuite]] -help [list [dict get $HELPTEXTS buildsuite]]"\
{@form -synopsis "make.tcl buildsuite list|info|build ?<suitename>? ?driver-args ...?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
"Action (list|info|build), suite name, and - for build - driver
arguments forwarded to the suite's suite.tcl untouched."}
punk::args::define\ punk::args::define\
"@id -id (script)::punkboot::help"\ "@id -id (script)::punkboot::help"\
"@normalize"\ "@normalize"\
@ -2099,6 +2137,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
"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}
"buildsuites" {buildsuite}
"informational" {info check projectversion workflow help} "informational" {info check projectversion workflow help}
"interactive" {shell} "interactive" {shell}
"deprecated aliases" {project vfs} "deprecated aliases" {project vfs}
@ -2184,6 +2223,11 @@ if {$::punkboot::punkargs_ok} {
} elseif {$subcommand eq "shell"} { } elseif {$subcommand eq "shell"} {
#declared passthrough: everything after 'shell' goes to the repl unparsed #declared passthrough: everything after 'shell' goes to the repl unparsed
set ::punkboot::command shell set ::punkboot::command shell
} elseif {$subcommand eq "buildsuite"} {
#declared passthrough: the handler parses action/name itself; driver
#args (arbitrary suite flags) are forwarded untouched (G-104)
set ::punkboot::command buildsuite
set ::punkboot::bs_args $subargs
} else { } else {
if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::$subcommand} argd]} { if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::$subcommand} argd]} {
puts stderr $argd puts stderr $argd
@ -2204,6 +2248,13 @@ if {$::punkboot::punkargs_ok} {
#unavailable). Keeps check/bootsupport/modules and the -k/-dirty-abort/-confirm flags working. #unavailable). Keeps check/bootsupport/modules and the -k/-dirty-abort/-confirm flags working.
set commands_found [list] set commands_found [list]
set argsleft $scriptargs set argsleft $scriptargs
if {[lindex $scriptargs 0] eq "buildsuite"} {
#declared passthrough (as in the punk::args path): driver args carry
#arbitrary suite flags the fallback flag scan must not consume (G-104)
set commands_found [list buildsuite]
set argsleft [list]
set ::punkboot::bs_args [lrange $scriptargs 1 end]
}
while {[llength $argsleft]} { while {[llength $argsleft]} {
set a [lindex $argsleft 0] set a [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end] set argsleft [lrange $argsleft 1 end]
@ -2319,7 +2370,7 @@ set forcekill $::punkboot::opt_forcekill
# - patch bump -> warn and proceed (non-breaking) # - patch bump -> warn and proceed (non-breaking)
# 'check' is exempt in all cases the warning is shown at the end of its output instead. # 'check' is exempt in all cases the warning is shown at the end of its output instead.
# 'projectversion' and 'workflow' are informational/read-only and are likewise exempt. # 'projectversion' and 'workflow' are informational/read-only and are likewise exempt.
if {[info exists ::punkboot::stale_bootsupport] && $::punkboot::command ni {check projectversion workflow}} { if {[info exists ::punkboot::stale_bootsupport] && $::punkboot::command ni {check projectversion workflow buildsuite}} {
set _stale $::punkboot::stale_bootsupport set _stale $::punkboot::stale_bootsupport
set _have_major 0 set _have_major 0
set _have_minor 0 set _have_minor 0
@ -2867,6 +2918,160 @@ if {$::punkboot::command eq "workflow"} {
exit 0 exit 0
} }
if {$::punkboot::command eq "buildsuite"} {
#G-104: thin front for the buildsuites (zig runtime factory - arm's-length
#subsystem building Tcl runtimes from external sources). Discovery is a
#directory scan; description/info come from each suite's sources.config
#records (description/product/doc/zigpin - the same manifest the suite.tcl
#driver parses), so copied/retargeted trees are first-class with no edits
#here. Contract: src/buildsuites/README.md.
set bs_root [file join $sourcefolder buildsuites]
if {![info exists ::punkboot::bs_args]} {set ::punkboot::bs_args [list]}
set bsargs $::punkboot::bs_args
set bs_action [lindex $bsargs 0]
if {$bs_action eq ""} {set bs_action list}
#tolerant manifest reader (make.tcl side): a suite with a malformed or absent
#sources.config still lists - with the problem as its description - rather
#than breaking discovery of the others. suite.tcl's own parser stays strict.
proc ::punkboot::buildsuite_manifest {suitedir} {
set m [dict create description "" products [list] docs [list] zigpin "" sources [list] note ""]
set cfg [file join $suitedir sources.config]
if {![file exists $cfg]} {
dict set m note "(no sources.config manifest)"
return $m
}
if {[catch {
set f [open $cfg r]; set data [read $f]; close $f
foreach line [split $data \n] {
set line [string trim $line]
if {$line eq "" || [string index $line 0] eq "#"} {continue}
if {[catch {llength $line}]} {continue}
switch -- [lindex $line 0] {
source {dict lappend m sources [lrange $line 1 end]}
description {dict set m description [join [lrange $line 1 end] " "]}
product {dict lappend m products [join [lrange $line 1 end] " "]}
doc {dict lappend m docs [lindex $line 1]}
zigpin {dict set m zigpin [lindex $line 1]}
}
}
} cfgerr]} {
dict set m note "(sources.config unreadable: $cfgerr)"
}
return $m
}
proc ::punkboot::buildsuite_dirs {bs_root} {
set dirs [list]
foreach dir [lsort [glob -nocomplain -dir $bs_root -type d *]] {
if {[file tail $dir] eq "_build"} {continue}
if {![file exists [file join $dir suite.tcl]]} {continue} ;#not a suite (no driver)
lappend dirs $dir
}
return $dirs
}
switch -- $bs_action {
list {
set suitedirs [::punkboot::buildsuite_dirs $bs_root]
if {![llength $suitedirs]} {
puts stdout "no buildsuites found under $bs_root (a suite = a directory carrying a suite.tcl driver)"
exit 0
}
puts stdout "buildsuites under $bs_root:"
foreach dir $suitedirs {
set m [::punkboot::buildsuite_manifest $dir]
set desc [dict get $m description]
if {$desc eq ""} {set desc [dict get $m note]}
if {$desc eq ""} {set desc "(no description record in sources.config)"}
puts stdout [format " %-16s %s" [file tail $dir] $desc]
}
puts stdout ""
puts stdout " - 'make.tcl buildsuite info <name>' shows a suite's configured detail"
puts stdout " - 'make.tcl buildsuite build <name> ?driver-args ...?' runs its driver"
exit 0
}
info {
set bs_name [lindex $bsargs 1]
if {$bs_name eq ""} {
puts stderr "usage: make.tcl buildsuite info <suitename> ('make.tcl buildsuite list' shows the names)"
exit 2
}
set dir [file join $bs_root $bs_name]
if {![file isdirectory $dir] || ![file exists [file join $dir suite.tcl]]} {
puts stderr "buildsuite '$bs_name' not found (no [file join $dir suite.tcl]) - 'make.tcl buildsuite list' shows the suites"
exit 2
}
set m [::punkboot::buildsuite_manifest $dir]
puts stdout "buildsuite: $bs_name"
puts stdout " path: $dir"
if {[dict get $m note] ne ""} {puts stdout " note: [dict get $m note]"}
if {[dict get $m description] ne ""} {puts stdout " description: [dict get $m description]"}
if {[llength [dict get $m sources]]} {
puts stdout " sources (sources.config - the live dev-flow refs; the 'zig build' flow pins per-checkin via build.zig.zon):"
puts stdout [format " %-10s %-7s %-40s %-16s %s" name kind url ref dir]
foreach s [dict get $m sources] {
lassign $s sname skind surl sref sdir
puts stdout [format " %-10s %-7s %-40s %-16s %s" $sname $skind $surl $sref $sdir]
}
}
set zigpin [dict get $m zigpin]
if {$zigpin ne ""} {
set pinpath [file normalize [file join $projectroot $zigpin]]
set pinstate [expr {[file exists $pinpath] ? "present" : "NOT PRESENT (bin/punk-getzig.cmd can fetch it)"}]
puts stdout " zig pin: $zigpin ($pinstate)"
} else {
puts stdout " zig pin: (no zigpin record in sources.config)"
}
if {[info exists ::env(PUNK_ZIG)]} {
puts stdout " zig via env: PUNK_ZIG=$::env(PUNK_ZIG) (overrides the pin for driver runs)"
}
foreach p [dict get $m products] {
puts stdout " product: $p"
}
foreach d [dict get $m docs] {
puts stdout " doc: [file join $dir $d]"
}
exit 0
}
build {
set bs_name [lindex $bsargs 1]
if {$bs_name eq ""} {
puts stderr "usage: make.tcl buildsuite build <suitename> ?driver-args ...?"
exit 2
}
set dir [file join $bs_root $bs_name]
set driver [file join $dir suite.tcl]
if {![file exists $driver]} {
puts stderr "buildsuite '$bs_name' not found (no $driver) - 'make.tcl buildsuite list' shows the suites"
exit 2
}
set fwdargs [lrange $bsargs 2 end]
#interpreter for the driver: this tclsh - or, punk-exe-hosted (the
#G-058 static-capture marker identifies a kit boot), the kit's
#'script' subcommand so the driver runs with plain script semantics
set launcher [list [info nameofexecutable]]
if {[info exists ::punkboot::static_packages]} {
set launcher [list [info nameofexecutable] script]
}
puts stdout "buildsuite build $bs_name: running '$driver build' with forwarded args: $fwdargs"
flush stdout
#stream driver output; exit with the driver's status
if {[catch {exec {*}$launcher $driver build {*}$fwdargs >@stdout 2>@stderr} execerr execopts]} {
set ecode [dict get $execopts -errorcode]
if {[lindex $ecode 0] eq "CHILDSTATUS"} {
exit [lindex $ecode 2]
}
puts stderr "buildsuite build $bs_name: failed to run the driver: $execerr"
exit 1
}
exit 0
}
default {
puts stderr "unknown buildsuite action '$bs_action' - expected list|info|build"
puts stderr "usage: make.tcl buildsuite list|info|build ?<suitename>? ?driver-args ...?"
exit 2
}
}
}
if {$::punkboot::command eq "shell"} { if {$::punkboot::command eq "shell"} {
#G-113: the ansistrip transform serves make.tcl's own output only - the repl's #G-113: the ansistrip transform serves make.tcl's own output only - the repl's
#colour behaviour is the shell's own concern (colour on/off + NO_COLOR there). #colour behaviour is the shell's own concern (colour on/off + NO_COLOR there).

Loading…
Cancel
Save