From 56a0567a0da506855b6d76502cea83f1ea6ea083 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Sat, 1 Aug 2026 22:44:29 +1000 Subject: [PATCH] Build outputs: thin-layout make.tcl sync (G-143 argdoc/help + G-144 synopsis retirement) punkcheck-managed copies refreshed by 'make.tcl modules' - vendor layouts basic + project-0.1 and the modpod-shipped template payload, all byte-identical to src/make.tcl. Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com --- .../vendor/punk/project-0.1/src/make.tcl | 251 +++++++++++++----- .../vendor/punk/basic/src/make.tcl | 251 +++++++++++++----- .../vendor/punk/project-0.1/src/make.tcl | 251 +++++++++++++----- 3 files changed, 558 insertions(+), 195 deletions(-) diff --git a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl index 8df9a39c..49d50aec 100644 --- a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl +++ b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl @@ -2610,13 +2610,18 @@ proc ::punkboot::punkboot_gethelp {args} { append h " $scriptname buildsuite list|info|build ?? ?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 tool list|info|build|test ? ...? ?-test 0|1?" \n + append h " $scriptname tool list ? ...?" \n + append h " $scriptname tool info ..." \n + append h " $scriptname tool build ?-test 0|1? ? ...?" \n + append h " $scriptname tool test ? ...?" \n append h " - vendored first-party build tools under src/tools (zig source; G-126): list/detail them, run their" \n - append h " test suites, or build (test-gated) and install to /bin" \n + append h " test suites, or build (test-gated) and install to /bin; options precede the tool names" \n append h " - zig is OPTIONAL: packages/bake never require this step; without a suitable toolchain the state is" \n append h " reported and build/test exit nonzero with fetch guidance (bin/punk-getzig.cmd)" \n \n - append h " $scriptname help ?subcommand?" \n - append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname -help)" \n \n + append h " $scriptname help ?subcommand? ?action?" \n + append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname -help);" \n + append h " with an action word or 0-based form index, that action's single-form usage when the tabled help" \n + append h " is available" \n \n append h " Flags:" \n append h " -confirm 0|1" \n append h " - interactive y/n confirmation policy (default 1: prompt when stdin is a terminal, abort fast when it is not)." \n @@ -3234,7 +3239,11 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO help " Show tabled usage for make.tcl as a whole, or for a single subcommand. - Equivalent to 'make.tcl -help'." + Equivalent to 'make.tcl -help'. For the multi-form + subcommands (tool, buildsuite) an action word - or the 0-based form + index the synopsis hints display - narrows to that action's + single-form usage: 'make.tcl help tool build', 'make.tcl help tool 2' + and 'make.tcl tool build -help' all render the build form." buildsuite " Surface for the defined buildsuites under src/buildsuites (zig-built @@ -3278,16 +3287,19 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO tool info ... Per-tool detail: paths, provenance records, toolchain resolution. - tool build ? ...? ?-test 0|1? + tool build ?-test 0|1? ? ...? Resolve a zig satisfying the tool's declared floor, run the 'zig build test' gate (default on; the gate is the EXIT CODE - expected warnings on stderr do not fail it), build ReleaseSafe and install to /bin. Nothing is installed when the - gate or the build fails. With no name, all tools are + gate or the build fails. Options precede the tool names + (punk::args positional model); with no name, all tools are processed. tool test ? ...? Run each tool's 'zig build test' only. + 'make.tcl help tool ' shows a single action's usage table. + Toolchain resolution: PUNK_ZIG= overrides; otherwise /bin/tools/zig* is scanned and the LOWEST release satisfying the floor wins (deterministic as @@ -3404,7 +3416,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bakehouse]}" -& -help -& {${[dict get $HELPTEXTS bakehouse]}} - @form -synopsis "make.tcl bakehouse ?-k? ?-dirty-abort 1|0? ?-confirm 0|1?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT_ON} @@ -3417,7 +3428,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES packages]}" -& -help -& {${[dict get $HELPTEXTS packages]}} - @form -synopsis "make.tcl packages ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3429,7 +3439,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES modules]}" -& -help -& {${[dict get $HELPTEXTS modules]}} - @form -synopsis "make.tcl modules ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3441,7 +3450,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES libs]}" -& -help -& {${[dict get $HELPTEXTS libs]}} - @form -synopsis "make.tcl libs ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3453,7 +3461,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bake]}" -& -help -& {${[dict get $HELPTEXTS bake]}} - @form -synopsis "make.tcl bake ?-k? ?-dirty-abort? ?-confirm 0|1? ?kitname ...?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT} @@ -3472,7 +3479,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bakelist]}" -& -help -& {${[dict get $HELPTEXTS bakelist]}} - @form -synopsis "make.tcl bakelist ?kitname ...?" @values -min 0 -max -1 kitname -type string -optional 1 -multiple 1${$KITNAME_CHOICEPART} -help -& "Configured kit output(s) to report on - a kit name, or @ @@ -3486,7 +3492,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vfslibs]}" -& -help -& {${[dict get $HELPTEXTS vfslibs]}} - @form -synopsis "make.tcl vfslibs ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3498,7 +3503,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bin]}" -& -help -& {${[dict get $HELPTEXTS bin]}} - @form -synopsis "make.tcl bin ?-k? ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT} @@ -3511,7 +3515,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vendorupdate]}" -& -help -& {${[dict get $HELPTEXTS vendorupdate]}} - @form -synopsis "make.tcl vendorupdate ?-confirm 0|1?" @opts ${$OPT_CONFIRM} @values -min 0 -max 0 @@ -3522,7 +3525,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES libfetch]}" -& -help -& {${[dict get $HELPTEXTS libfetch]}} - @form -synopsis "make.tcl libfetch ?-serverurl ? ?-trust-server? ?-force?" @opts ${$OPT_SERVERURL} ${$OPT_TRUSTSERVER} @@ -3535,7 +3537,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bootsupport]}" -& -help -& {${[dict get $HELPTEXTS bootsupport]}} - @form -synopsis "make.tcl bootsupport ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3547,7 +3548,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vfscommonupdate]}" -& -help -& {${[dict get $HELPTEXTS vfscommonupdate]}} - @form -synopsis "make.tcl vfscommonupdate ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3559,7 +3559,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES info]}" -& -help -& {${[dict get $HELPTEXTS info]}} - @form -synopsis "make.tcl info ?-confirm 0|1?" @opts ${$OPT_CONFIRM} @values -min 0 -max 0 @@ -3570,7 +3569,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES check]}" -& -help -& {${[dict get $HELPTEXTS check]}} - @form -synopsis "make.tcl check" @opts @values -min 0 -max 0 } @@ -3580,7 +3578,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES projectversion]}" -& -help -& {${[dict get $HELPTEXTS projectversion]}} - @form -synopsis "make.tcl projectversion" @opts @values -min 0 -max 0 } @@ -3590,7 +3587,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES workflow]}" -& -help -& {${[dict get $HELPTEXTS workflow]}} - @form -synopsis "make.tcl workflow" @opts @values -min 0 -max 0 } @@ -3602,7 +3598,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO "Run the punk shell (repl) using bootsupport libraries. All arguments after the shell subcommand are passed through to the repl untouched (they are not parsed or validated by make.tcl)." - @form -synopsis "make.tcl shell ?arg ...?" @values -min 0 -max -1 arg -type any -optional 1 -multiple 1 -help -& "Arguments passed through to the punk repl." @@ -3613,11 +3608,28 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES buildsuite]}" -& -help -& {${[dict get $HELPTEXTS buildsuite]}} - @form -synopsis "make.tcl buildsuite list|info|build ?? ?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." + @form -form {list info build} + @form -form list + @leaders -min 1 -max 1 + action -choices {list} -choicelabels -& + {list "Discover the suites (directories carrying a suite.tcl driver) with their one-line descriptions"} + @values -min 0 -max 0 + @form -form info + @leaders -min 1 -max 1 + action -choices {info} -choicelabels -& + {info "Show a suite's configured detail: description, source records, zig pin, products, doc pointers"} + @values -min 1 -max 1 + suitename -type string -optional 0 -help -& + "Suite name (a directory under src/buildsuites carrying a suite.tcl driver)." + @form -form build + @leaders -min 1 -max 1 + action -choices {build} -choicelabels -& + {build "Run the suite's driver ('suite.tcl build'), forwarding trailing args untouched"} + @values -min 1 -max -1 + suitename -type string -optional 0 -help -& + "Suite to build." + driverarg -type any -optional 1 -multiple 1 -help -& + "Driver arguments forwarded to suite.tcl untouched (e.g -tclbranch/-refresh/-steps)." } punk::args::define { @@ -3626,11 +3638,38 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES tool]}" -& -help -& {${[dict get $HELPTEXTS tool]}} - @form -synopsis "make.tcl tool list|info|build|test ? ...? ?-test 0|1?" + @form -form {list info build test} + @form -form list + @leaders -min 1 -max 1 + action -choices {list} -choicelabels -& + {list "Discover the tools with version, zig floor, install state and vendored commit; report the resolved toolchain"} @values -min 0 -max -1 - arg -type any -optional 1 -multiple 1 -help -& - "Action (list|info|build|test), tool name(s), and - for build - - the -test 0|1 gate flag. Parsed by the handler." + toolname -type string -optional 1 -multiple 1 -& + -help "Validated against the configured tools (list reports all regardless)." + @form -form info + @leaders -min 1 -max 1 + action -choices {info} -choicelabels -& + {info "Per-tool detail: paths, provenance records, toolchain resolution"} + @values -min 1 -max -1 + toolname -type string -optional 0 -multiple 1 -& + -help "Tool(s) to describe (directory names under src/tools)." + @form -form build + @leaders -min 1 -max 1 + action -choices {build} -choicelabels -& + {build "Resolve zig, run the test gate, build ReleaseSafe and install to /bin"} + @opts + -test -type boolean -default 1 -help -& + "Run the 'zig build test' gate before building (default 1). The gate is the exit code." + @values -min 0 -max -1 + toolname -type string -optional 1 -multiple 1 -& + -help "Tool(s) to build; with no name, all tools are processed." + @form -form test + @leaders -min 1 -max 1 + action -choices {test} -choicelabels -& + {test "Run each tool's 'zig build test' only"} + @values -min 0 -max -1 + toolname -type string -optional 1 -multiple 1 -& + -help "Tool(s) to test; with no name, all tools are processed." } punk::args::define { @@ -3639,11 +3678,13 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES help]}" -& -help -& {${[dict get $HELPTEXTS help]}} - @form -synopsis "make.tcl help ?subcommand?" - @values -min 1 -max 1 + @values -min 1 -max 2 subject -type string -optional 0 -& -choices {${[dict keys $SUMMARIES]}} -& -help "Subcommand to show usage for." + action -type string -optional 1 -help -& + "Action word (or 0-based form index) of a multi-form subcommand + (tool, buildsuite) - shows that action's single-form usage." } #top-level definition: the subcommand table rendered by 'make.tcl' / 'make.tcl -help' variable SUBGROUPS { @@ -3664,16 +3705,21 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO 'bin' and 'modules' folders at the same level as 'src'. General usage: make.tcl ?flags? - 'make.tcl help ' or 'make.tcl -help' shows a - subcommand's own usage. Interactive y/n confirmations can be driven - non-interactively with the -confirm flag declared on the relevant - subcommands.} + 'make.tcl help ?action?' or 'make.tcl ?action? -help' + shows a subcommand's own usage (a single action's usage for the + multi-form tool/buildsuite subcommands). Interactive y/n + confirmations can be driven non-interactively with the -confirm + flag declared on the relevant subcommands.} punk::args::define { @id -id "(script)::punkboot" @cmd -name "make.tcl" -& -summary "punkshell project build tool (punk boot)" -& -help -& {${$TOPLEVEL_HELP}} + #sole remaining explicit synopsis override (G-143 retired the per-subcommand + #ones per G-144's @cmd -name auto-synopsis): the auto render would show the + #subcommand leader as mandatory - this line states the bare-invocation and + #per-subcommand-flags reality the declaration cannot. @form -synopsis "make.tcl ?subcommand? ?flags?" @leaders -min 1 -max 1 subcommand -type string -optional 0 -choicecolumns 1 -& @@ -3708,6 +3754,7 @@ if {$::punkboot::punkargs_ok && [package provide punk::ansi] ne "" && [package p set do_help 0 set help_subject "" +set help_action "" set help_exitcode 0 #defaults for the option-derived variables (fallback scan and passthrough subcommands rely on these) set ::punkboot::opt_forcekill 0 @@ -3743,10 +3790,26 @@ if {$::punkboot::punkargs_ok} { exit 1 } set help_subject [dict get $argd values subject] + if {[dict exists $argd values action]} { + #G-143: 'make.tcl help ' renders the action's + #single-form usage (resolved against the subject's forms below) + set help_action [dict get $argd values action] + } } } elseif {$wants_help} { set do_help 1 set help_subject $subcommand + #G-143: ' -help' routes to the action's single-form + #usage when the subcommand is declared multi-form (tool, buildsuite) - + #the action candidate is the first non-flag word. Single-form subcommands + #keep the historic whole-subcommand render (trailing words ignored). + set firstword "" + foreach w $subargs { + if {![string match -* $w]} {set firstword $w ; break} + } + if {$firstword ne "" && [llength [punk::args::forms (script)::punkboot::$subcommand]] > 1} { + set help_action $firstword + } } elseif {$subcommand eq "shell"} { #declared passthrough: everything after 'shell' goes to the repl unparsed set ::punkboot::command shell @@ -3756,9 +3819,29 @@ if {$::punkboot::punkargs_ok} { set ::punkboot::command buildsuite set ::punkboot::bs_args $subargs } elseif {$subcommand eq "tool"} { - #declared passthrough: the handler parses action/names/-test itself (G-126) + #G-143: tool is declared multi-form (one @form per action) and dispatch + #parses through the definition - unknown actions and unknown flags in + #option position produce pointed punk::args usage errors (exit 1). The + #definition's positional model puts -test before the tool names + #('make.tcl tool build -test 0 ...'); the historic flag-anywhere + #order is not carried over (docs updated). Bare 'tool' selects the list + #form. The degraded plain scan keeps the historic manual tail parse + #(handler-side, exit-2 surface preserved there). + if {![llength $subargs]} {set subargs [list list]} + if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::tool} argd]} { + puts stderr $argd + exit 1 + } set ::punkboot::command tool - set ::punkboot::tool_args $subargs + set ::punkboot::tool_action [dict get $argd form] + set ::punkboot::tool_names [list] + if {[dict exists $argd values toolname]} { + set ::punkboot::tool_names [dict get $argd values toolname] + } + set ::punkboot::tool_dotest 1 + if {[dict exists $argd opts -test]} { + set ::punkboot::tool_dotest [dict get $argd opts -test] + } } else { if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::$subcommand} argd]} { puts stderr $argd @@ -3871,7 +3954,32 @@ if {$do_help} { if {$help_subject ne ""} { set usage_id (script)::punkboot::$help_subject } - if {![catch {punk::args::usage $usage_id} usage_out]} { + set usage_args [list] + if {$help_action ne ""} { + #G-143: per-action single-form usage. An unknown action word is a pointed + #punk::args usage error (exit 1) - never a plain-help fallback. Form + #names resolve by unambiguous prefix, matching the dispatch convention; + #a 0-based form INDEX is also accepted - the notation punk::args itself + #advertises (the ' i -form N ...' synopsis hints and its -form error + #messages both speak indexes). + set form_names [punk::args::forms $usage_id] + set fmatch [tcl::prefix::match -error "" $form_names $help_action] + if {$fmatch eq "" && [string is integer -strict $help_action] + && $help_action >= 0 && $help_action < [llength $form_names]} { + set fmatch [lindex $form_names $help_action] + } + if {$fmatch eq ""} { + if {[catch {punk::args::usage -form $help_action $usage_id} usage_err]} { + puts stderr $usage_err + } else { + puts stderr "make.tcl: no action '$help_action' for '$help_subject' (actions: $form_names)" + } + exit 1 + } + set help_action $fmatch + lappend usage_args -form $help_action + } + if {![catch {punk::args::usage {*}$usage_args $usage_id} usage_out]} { puts stdout $usage_out set availability_note [::punkboot::punkboot_availability_note] if {$availability_note ne ""} { @@ -5126,30 +5234,40 @@ if {$::punkboot::command eq "tool"} { #fetch guidance. Build caches (.zig-cache/zig-out) are deliberately left in #place between builds for rebuild speed (both git- and fossil-ignored). set tool_root [file join $sourcefolder tools] - if {![info exists ::punkboot::tool_args]} {set ::punkboot::tool_args [list]} - set targs $::punkboot::tool_args - set tool_action [lindex $targs 0] - if {$tool_action eq ""} {set tool_action list} - #args after the action: tool names plus an optional '-test 0|1' flag (build) - set tool_dotest 1 - set tool_names [list] - set _i 1 - while {$_i < [llength $targs]} { - set a [lindex $targs $_i] - if {$a eq "-test"} { - incr _i - set v [lindex $targs $_i] - if {![string is boolean -strict $v]} { - puts stderr "make.tcl tool: -test requires a boolean value (got '$v')" - exit 2 + if {$::punkboot::punkargs_ok} { + #G-143: action/names/-test already parsed through the multi-form definition + #at dispatch (pointed punk::args usage errors for unknown actions/flags). + set tool_action $::punkboot::tool_action + set tool_names $::punkboot::tool_names + set tool_dotest $::punkboot::tool_dotest + } else { + #degraded plain scan: the handler's historic manual tail parse (its unknown + #action / bad -test exit-2 surface is preserved in this mode) + if {![info exists ::punkboot::tool_args]} {set ::punkboot::tool_args [list]} + set targs $::punkboot::tool_args + set tool_action [lindex $targs 0] + if {$tool_action eq ""} {set tool_action list} + #args after the action: tool names plus an optional '-test 0|1' flag (build) + set tool_dotest 1 + set tool_names [list] + set _i 1 + while {$_i < [llength $targs]} { + set a [lindex $targs $_i] + if {$a eq "-test"} { + incr _i + set v [lindex $targs $_i] + if {![string is boolean -strict $v]} { + puts stderr "make.tcl tool: -test requires a boolean value (got '$v')" + exit 2 + } + set tool_dotest [expr {bool($v)}] + } else { + lappend tool_names $a } - set tool_dotest [expr {bool($v)}] - } else { - lappend tool_names $a + incr _i } - incr _i + unset -nocomplain _i a v } - unset -nocomplain _i a v proc ::punkboot::tool_dirs {tool_root} { set dirs [list] @@ -5297,6 +5415,9 @@ if {$::punkboot::command eq "tool"} { set selected [list] foreach n $tool_names { if {![dict exists $byname $n]} { + if {[string match -* $n]} { + puts stderr "tool: '$n' looks like a flag - options precede the tool names (e.g 'make.tcl tool build -test 0 ...')" + } puts stderr "tool '$n' not found under $tool_root - configured tools: [dict keys $byname]" exit 2 } @@ -5340,7 +5461,7 @@ if {$::punkboot::command eq "tool"} { } puts stdout "" puts stdout " - 'make.tcl tool info ' shows a tool's detail" - puts stdout " - 'make.tcl tool build ? ...? ?-test 0|1?' builds (test-gated) and installs to bin/" + puts stdout " - 'make.tcl tool build ?-test 0|1? ? ...?' builds (test-gated) and installs to bin/" puts stdout " - zig is OPTIONAL for punkshell builds: packages/bake never require this step" exit 0 } @@ -5446,7 +5567,7 @@ if {$::punkboot::command eq "tool"} { } default { puts stderr "unknown tool action '$tool_action' - expected list|info|build|test" - puts stderr "usage: make.tcl tool list|info|build|test ? ...? ?-test 0|1?" + puts stderr "usage: make.tcl tool list|info|build|test ?-test 0|1? ? ...?" exit 2 } } diff --git a/src/project_layouts/vendor/punk/basic/src/make.tcl b/src/project_layouts/vendor/punk/basic/src/make.tcl index 8df9a39c..49d50aec 100644 --- a/src/project_layouts/vendor/punk/basic/src/make.tcl +++ b/src/project_layouts/vendor/punk/basic/src/make.tcl @@ -2610,13 +2610,18 @@ proc ::punkboot::punkboot_gethelp {args} { append h " $scriptname buildsuite list|info|build ?? ?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 tool list|info|build|test ? ...? ?-test 0|1?" \n + append h " $scriptname tool list ? ...?" \n + append h " $scriptname tool info ..." \n + append h " $scriptname tool build ?-test 0|1? ? ...?" \n + append h " $scriptname tool test ? ...?" \n append h " - vendored first-party build tools under src/tools (zig source; G-126): list/detail them, run their" \n - append h " test suites, or build (test-gated) and install to /bin" \n + append h " test suites, or build (test-gated) and install to /bin; options precede the tool names" \n append h " - zig is OPTIONAL: packages/bake never require this step; without a suitable toolchain the state is" \n append h " reported and build/test exit nonzero with fetch guidance (bin/punk-getzig.cmd)" \n \n - append h " $scriptname help ?subcommand?" \n - append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname -help)" \n \n + append h " $scriptname help ?subcommand? ?action?" \n + append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname -help);" \n + append h " with an action word or 0-based form index, that action's single-form usage when the tabled help" \n + append h " is available" \n \n append h " Flags:" \n append h " -confirm 0|1" \n append h " - interactive y/n confirmation policy (default 1: prompt when stdin is a terminal, abort fast when it is not)." \n @@ -3234,7 +3239,11 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO help " Show tabled usage for make.tcl as a whole, or for a single subcommand. - Equivalent to 'make.tcl -help'." + Equivalent to 'make.tcl -help'. For the multi-form + subcommands (tool, buildsuite) an action word - or the 0-based form + index the synopsis hints display - narrows to that action's + single-form usage: 'make.tcl help tool build', 'make.tcl help tool 2' + and 'make.tcl tool build -help' all render the build form." buildsuite " Surface for the defined buildsuites under src/buildsuites (zig-built @@ -3278,16 +3287,19 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO tool info ... Per-tool detail: paths, provenance records, toolchain resolution. - tool build ? ...? ?-test 0|1? + tool build ?-test 0|1? ? ...? Resolve a zig satisfying the tool's declared floor, run the 'zig build test' gate (default on; the gate is the EXIT CODE - expected warnings on stderr do not fail it), build ReleaseSafe and install to /bin. Nothing is installed when the - gate or the build fails. With no name, all tools are + gate or the build fails. Options precede the tool names + (punk::args positional model); with no name, all tools are processed. tool test ? ...? Run each tool's 'zig build test' only. + 'make.tcl help tool ' shows a single action's usage table. + Toolchain resolution: PUNK_ZIG= overrides; otherwise /bin/tools/zig* is scanned and the LOWEST release satisfying the floor wins (deterministic as @@ -3404,7 +3416,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bakehouse]}" -& -help -& {${[dict get $HELPTEXTS bakehouse]}} - @form -synopsis "make.tcl bakehouse ?-k? ?-dirty-abort 1|0? ?-confirm 0|1?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT_ON} @@ -3417,7 +3428,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES packages]}" -& -help -& {${[dict get $HELPTEXTS packages]}} - @form -synopsis "make.tcl packages ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3429,7 +3439,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES modules]}" -& -help -& {${[dict get $HELPTEXTS modules]}} - @form -synopsis "make.tcl modules ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3441,7 +3450,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES libs]}" -& -help -& {${[dict get $HELPTEXTS libs]}} - @form -synopsis "make.tcl libs ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3453,7 +3461,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bake]}" -& -help -& {${[dict get $HELPTEXTS bake]}} - @form -synopsis "make.tcl bake ?-k? ?-dirty-abort? ?-confirm 0|1? ?kitname ...?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT} @@ -3472,7 +3479,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bakelist]}" -& -help -& {${[dict get $HELPTEXTS bakelist]}} - @form -synopsis "make.tcl bakelist ?kitname ...?" @values -min 0 -max -1 kitname -type string -optional 1 -multiple 1${$KITNAME_CHOICEPART} -help -& "Configured kit output(s) to report on - a kit name, or @ @@ -3486,7 +3492,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vfslibs]}" -& -help -& {${[dict get $HELPTEXTS vfslibs]}} - @form -synopsis "make.tcl vfslibs ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3498,7 +3503,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bin]}" -& -help -& {${[dict get $HELPTEXTS bin]}} - @form -synopsis "make.tcl bin ?-k? ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT} @@ -3511,7 +3515,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vendorupdate]}" -& -help -& {${[dict get $HELPTEXTS vendorupdate]}} - @form -synopsis "make.tcl vendorupdate ?-confirm 0|1?" @opts ${$OPT_CONFIRM} @values -min 0 -max 0 @@ -3522,7 +3525,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES libfetch]}" -& -help -& {${[dict get $HELPTEXTS libfetch]}} - @form -synopsis "make.tcl libfetch ?-serverurl ? ?-trust-server? ?-force?" @opts ${$OPT_SERVERURL} ${$OPT_TRUSTSERVER} @@ -3535,7 +3537,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bootsupport]}" -& -help -& {${[dict get $HELPTEXTS bootsupport]}} - @form -synopsis "make.tcl bootsupport ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3547,7 +3548,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vfscommonupdate]}" -& -help -& {${[dict get $HELPTEXTS vfscommonupdate]}} - @form -synopsis "make.tcl vfscommonupdate ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3559,7 +3559,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES info]}" -& -help -& {${[dict get $HELPTEXTS info]}} - @form -synopsis "make.tcl info ?-confirm 0|1?" @opts ${$OPT_CONFIRM} @values -min 0 -max 0 @@ -3570,7 +3569,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES check]}" -& -help -& {${[dict get $HELPTEXTS check]}} - @form -synopsis "make.tcl check" @opts @values -min 0 -max 0 } @@ -3580,7 +3578,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES projectversion]}" -& -help -& {${[dict get $HELPTEXTS projectversion]}} - @form -synopsis "make.tcl projectversion" @opts @values -min 0 -max 0 } @@ -3590,7 +3587,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES workflow]}" -& -help -& {${[dict get $HELPTEXTS workflow]}} - @form -synopsis "make.tcl workflow" @opts @values -min 0 -max 0 } @@ -3602,7 +3598,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO "Run the punk shell (repl) using bootsupport libraries. All arguments after the shell subcommand are passed through to the repl untouched (they are not parsed or validated by make.tcl)." - @form -synopsis "make.tcl shell ?arg ...?" @values -min 0 -max -1 arg -type any -optional 1 -multiple 1 -help -& "Arguments passed through to the punk repl." @@ -3613,11 +3608,28 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES buildsuite]}" -& -help -& {${[dict get $HELPTEXTS buildsuite]}} - @form -synopsis "make.tcl buildsuite list|info|build ?? ?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." + @form -form {list info build} + @form -form list + @leaders -min 1 -max 1 + action -choices {list} -choicelabels -& + {list "Discover the suites (directories carrying a suite.tcl driver) with their one-line descriptions"} + @values -min 0 -max 0 + @form -form info + @leaders -min 1 -max 1 + action -choices {info} -choicelabels -& + {info "Show a suite's configured detail: description, source records, zig pin, products, doc pointers"} + @values -min 1 -max 1 + suitename -type string -optional 0 -help -& + "Suite name (a directory under src/buildsuites carrying a suite.tcl driver)." + @form -form build + @leaders -min 1 -max 1 + action -choices {build} -choicelabels -& + {build "Run the suite's driver ('suite.tcl build'), forwarding trailing args untouched"} + @values -min 1 -max -1 + suitename -type string -optional 0 -help -& + "Suite to build." + driverarg -type any -optional 1 -multiple 1 -help -& + "Driver arguments forwarded to suite.tcl untouched (e.g -tclbranch/-refresh/-steps)." } punk::args::define { @@ -3626,11 +3638,38 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES tool]}" -& -help -& {${[dict get $HELPTEXTS tool]}} - @form -synopsis "make.tcl tool list|info|build|test ? ...? ?-test 0|1?" + @form -form {list info build test} + @form -form list + @leaders -min 1 -max 1 + action -choices {list} -choicelabels -& + {list "Discover the tools with version, zig floor, install state and vendored commit; report the resolved toolchain"} @values -min 0 -max -1 - arg -type any -optional 1 -multiple 1 -help -& - "Action (list|info|build|test), tool name(s), and - for build - - the -test 0|1 gate flag. Parsed by the handler." + toolname -type string -optional 1 -multiple 1 -& + -help "Validated against the configured tools (list reports all regardless)." + @form -form info + @leaders -min 1 -max 1 + action -choices {info} -choicelabels -& + {info "Per-tool detail: paths, provenance records, toolchain resolution"} + @values -min 1 -max -1 + toolname -type string -optional 0 -multiple 1 -& + -help "Tool(s) to describe (directory names under src/tools)." + @form -form build + @leaders -min 1 -max 1 + action -choices {build} -choicelabels -& + {build "Resolve zig, run the test gate, build ReleaseSafe and install to /bin"} + @opts + -test -type boolean -default 1 -help -& + "Run the 'zig build test' gate before building (default 1). The gate is the exit code." + @values -min 0 -max -1 + toolname -type string -optional 1 -multiple 1 -& + -help "Tool(s) to build; with no name, all tools are processed." + @form -form test + @leaders -min 1 -max 1 + action -choices {test} -choicelabels -& + {test "Run each tool's 'zig build test' only"} + @values -min 0 -max -1 + toolname -type string -optional 1 -multiple 1 -& + -help "Tool(s) to test; with no name, all tools are processed." } punk::args::define { @@ -3639,11 +3678,13 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES help]}" -& -help -& {${[dict get $HELPTEXTS help]}} - @form -synopsis "make.tcl help ?subcommand?" - @values -min 1 -max 1 + @values -min 1 -max 2 subject -type string -optional 0 -& -choices {${[dict keys $SUMMARIES]}} -& -help "Subcommand to show usage for." + action -type string -optional 1 -help -& + "Action word (or 0-based form index) of a multi-form subcommand + (tool, buildsuite) - shows that action's single-form usage." } #top-level definition: the subcommand table rendered by 'make.tcl' / 'make.tcl -help' variable SUBGROUPS { @@ -3664,16 +3705,21 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO 'bin' and 'modules' folders at the same level as 'src'. General usage: make.tcl ?flags? - 'make.tcl help ' or 'make.tcl -help' shows a - subcommand's own usage. Interactive y/n confirmations can be driven - non-interactively with the -confirm flag declared on the relevant - subcommands.} + 'make.tcl help ?action?' or 'make.tcl ?action? -help' + shows a subcommand's own usage (a single action's usage for the + multi-form tool/buildsuite subcommands). Interactive y/n + confirmations can be driven non-interactively with the -confirm + flag declared on the relevant subcommands.} punk::args::define { @id -id "(script)::punkboot" @cmd -name "make.tcl" -& -summary "punkshell project build tool (punk boot)" -& -help -& {${$TOPLEVEL_HELP}} + #sole remaining explicit synopsis override (G-143 retired the per-subcommand + #ones per G-144's @cmd -name auto-synopsis): the auto render would show the + #subcommand leader as mandatory - this line states the bare-invocation and + #per-subcommand-flags reality the declaration cannot. @form -synopsis "make.tcl ?subcommand? ?flags?" @leaders -min 1 -max 1 subcommand -type string -optional 0 -choicecolumns 1 -& @@ -3708,6 +3754,7 @@ if {$::punkboot::punkargs_ok && [package provide punk::ansi] ne "" && [package p set do_help 0 set help_subject "" +set help_action "" set help_exitcode 0 #defaults for the option-derived variables (fallback scan and passthrough subcommands rely on these) set ::punkboot::opt_forcekill 0 @@ -3743,10 +3790,26 @@ if {$::punkboot::punkargs_ok} { exit 1 } set help_subject [dict get $argd values subject] + if {[dict exists $argd values action]} { + #G-143: 'make.tcl help ' renders the action's + #single-form usage (resolved against the subject's forms below) + set help_action [dict get $argd values action] + } } } elseif {$wants_help} { set do_help 1 set help_subject $subcommand + #G-143: ' -help' routes to the action's single-form + #usage when the subcommand is declared multi-form (tool, buildsuite) - + #the action candidate is the first non-flag word. Single-form subcommands + #keep the historic whole-subcommand render (trailing words ignored). + set firstword "" + foreach w $subargs { + if {![string match -* $w]} {set firstword $w ; break} + } + if {$firstword ne "" && [llength [punk::args::forms (script)::punkboot::$subcommand]] > 1} { + set help_action $firstword + } } elseif {$subcommand eq "shell"} { #declared passthrough: everything after 'shell' goes to the repl unparsed set ::punkboot::command shell @@ -3756,9 +3819,29 @@ if {$::punkboot::punkargs_ok} { set ::punkboot::command buildsuite set ::punkboot::bs_args $subargs } elseif {$subcommand eq "tool"} { - #declared passthrough: the handler parses action/names/-test itself (G-126) + #G-143: tool is declared multi-form (one @form per action) and dispatch + #parses through the definition - unknown actions and unknown flags in + #option position produce pointed punk::args usage errors (exit 1). The + #definition's positional model puts -test before the tool names + #('make.tcl tool build -test 0 ...'); the historic flag-anywhere + #order is not carried over (docs updated). Bare 'tool' selects the list + #form. The degraded plain scan keeps the historic manual tail parse + #(handler-side, exit-2 surface preserved there). + if {![llength $subargs]} {set subargs [list list]} + if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::tool} argd]} { + puts stderr $argd + exit 1 + } set ::punkboot::command tool - set ::punkboot::tool_args $subargs + set ::punkboot::tool_action [dict get $argd form] + set ::punkboot::tool_names [list] + if {[dict exists $argd values toolname]} { + set ::punkboot::tool_names [dict get $argd values toolname] + } + set ::punkboot::tool_dotest 1 + if {[dict exists $argd opts -test]} { + set ::punkboot::tool_dotest [dict get $argd opts -test] + } } else { if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::$subcommand} argd]} { puts stderr $argd @@ -3871,7 +3954,32 @@ if {$do_help} { if {$help_subject ne ""} { set usage_id (script)::punkboot::$help_subject } - if {![catch {punk::args::usage $usage_id} usage_out]} { + set usage_args [list] + if {$help_action ne ""} { + #G-143: per-action single-form usage. An unknown action word is a pointed + #punk::args usage error (exit 1) - never a plain-help fallback. Form + #names resolve by unambiguous prefix, matching the dispatch convention; + #a 0-based form INDEX is also accepted - the notation punk::args itself + #advertises (the ' i -form N ...' synopsis hints and its -form error + #messages both speak indexes). + set form_names [punk::args::forms $usage_id] + set fmatch [tcl::prefix::match -error "" $form_names $help_action] + if {$fmatch eq "" && [string is integer -strict $help_action] + && $help_action >= 0 && $help_action < [llength $form_names]} { + set fmatch [lindex $form_names $help_action] + } + if {$fmatch eq ""} { + if {[catch {punk::args::usage -form $help_action $usage_id} usage_err]} { + puts stderr $usage_err + } else { + puts stderr "make.tcl: no action '$help_action' for '$help_subject' (actions: $form_names)" + } + exit 1 + } + set help_action $fmatch + lappend usage_args -form $help_action + } + if {![catch {punk::args::usage {*}$usage_args $usage_id} usage_out]} { puts stdout $usage_out set availability_note [::punkboot::punkboot_availability_note] if {$availability_note ne ""} { @@ -5126,30 +5234,40 @@ if {$::punkboot::command eq "tool"} { #fetch guidance. Build caches (.zig-cache/zig-out) are deliberately left in #place between builds for rebuild speed (both git- and fossil-ignored). set tool_root [file join $sourcefolder tools] - if {![info exists ::punkboot::tool_args]} {set ::punkboot::tool_args [list]} - set targs $::punkboot::tool_args - set tool_action [lindex $targs 0] - if {$tool_action eq ""} {set tool_action list} - #args after the action: tool names plus an optional '-test 0|1' flag (build) - set tool_dotest 1 - set tool_names [list] - set _i 1 - while {$_i < [llength $targs]} { - set a [lindex $targs $_i] - if {$a eq "-test"} { - incr _i - set v [lindex $targs $_i] - if {![string is boolean -strict $v]} { - puts stderr "make.tcl tool: -test requires a boolean value (got '$v')" - exit 2 + if {$::punkboot::punkargs_ok} { + #G-143: action/names/-test already parsed through the multi-form definition + #at dispatch (pointed punk::args usage errors for unknown actions/flags). + set tool_action $::punkboot::tool_action + set tool_names $::punkboot::tool_names + set tool_dotest $::punkboot::tool_dotest + } else { + #degraded plain scan: the handler's historic manual tail parse (its unknown + #action / bad -test exit-2 surface is preserved in this mode) + if {![info exists ::punkboot::tool_args]} {set ::punkboot::tool_args [list]} + set targs $::punkboot::tool_args + set tool_action [lindex $targs 0] + if {$tool_action eq ""} {set tool_action list} + #args after the action: tool names plus an optional '-test 0|1' flag (build) + set tool_dotest 1 + set tool_names [list] + set _i 1 + while {$_i < [llength $targs]} { + set a [lindex $targs $_i] + if {$a eq "-test"} { + incr _i + set v [lindex $targs $_i] + if {![string is boolean -strict $v]} { + puts stderr "make.tcl tool: -test requires a boolean value (got '$v')" + exit 2 + } + set tool_dotest [expr {bool($v)}] + } else { + lappend tool_names $a } - set tool_dotest [expr {bool($v)}] - } else { - lappend tool_names $a + incr _i } - incr _i + unset -nocomplain _i a v } - unset -nocomplain _i a v proc ::punkboot::tool_dirs {tool_root} { set dirs [list] @@ -5297,6 +5415,9 @@ if {$::punkboot::command eq "tool"} { set selected [list] foreach n $tool_names { if {![dict exists $byname $n]} { + if {[string match -* $n]} { + puts stderr "tool: '$n' looks like a flag - options precede the tool names (e.g 'make.tcl tool build -test 0 ...')" + } puts stderr "tool '$n' not found under $tool_root - configured tools: [dict keys $byname]" exit 2 } @@ -5340,7 +5461,7 @@ if {$::punkboot::command eq "tool"} { } puts stdout "" puts stdout " - 'make.tcl tool info ' shows a tool's detail" - puts stdout " - 'make.tcl tool build ? ...? ?-test 0|1?' builds (test-gated) and installs to bin/" + puts stdout " - 'make.tcl tool build ?-test 0|1? ? ...?' builds (test-gated) and installs to bin/" puts stdout " - zig is OPTIONAL for punkshell builds: packages/bake never require this step" exit 0 } @@ -5446,7 +5567,7 @@ if {$::punkboot::command eq "tool"} { } default { puts stderr "unknown tool action '$tool_action' - expected list|info|build|test" - puts stderr "usage: make.tcl tool list|info|build|test ? ...? ?-test 0|1?" + puts stderr "usage: make.tcl tool list|info|build|test ?-test 0|1? ? ...?" exit 2 } } diff --git a/src/project_layouts/vendor/punk/project-0.1/src/make.tcl b/src/project_layouts/vendor/punk/project-0.1/src/make.tcl index 8df9a39c..49d50aec 100644 --- a/src/project_layouts/vendor/punk/project-0.1/src/make.tcl +++ b/src/project_layouts/vendor/punk/project-0.1/src/make.tcl @@ -2610,13 +2610,18 @@ proc ::punkboot::punkboot_gethelp {args} { append h " $scriptname buildsuite list|info|build ?? ?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 tool list|info|build|test ? ...? ?-test 0|1?" \n + append h " $scriptname tool list ? ...?" \n + append h " $scriptname tool info ..." \n + append h " $scriptname tool build ?-test 0|1? ? ...?" \n + append h " $scriptname tool test ? ...?" \n append h " - vendored first-party build tools under src/tools (zig source; G-126): list/detail them, run their" \n - append h " test suites, or build (test-gated) and install to /bin" \n + append h " test suites, or build (test-gated) and install to /bin; options precede the tool names" \n append h " - zig is OPTIONAL: packages/bake never require this step; without a suitable toolchain the state is" \n append h " reported and build/test exit nonzero with fetch guidance (bin/punk-getzig.cmd)" \n \n - append h " $scriptname help ?subcommand?" \n - append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname -help)" \n \n + append h " $scriptname help ?subcommand? ?action?" \n + append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname -help);" \n + append h " with an action word or 0-based form index, that action's single-form usage when the tabled help" \n + append h " is available" \n \n append h " Flags:" \n append h " -confirm 0|1" \n append h " - interactive y/n confirmation policy (default 1: prompt when stdin is a terminal, abort fast when it is not)." \n @@ -3234,7 +3239,11 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO help " Show tabled usage for make.tcl as a whole, or for a single subcommand. - Equivalent to 'make.tcl -help'." + Equivalent to 'make.tcl -help'. For the multi-form + subcommands (tool, buildsuite) an action word - or the 0-based form + index the synopsis hints display - narrows to that action's + single-form usage: 'make.tcl help tool build', 'make.tcl help tool 2' + and 'make.tcl tool build -help' all render the build form." buildsuite " Surface for the defined buildsuites under src/buildsuites (zig-built @@ -3278,16 +3287,19 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO tool info ... Per-tool detail: paths, provenance records, toolchain resolution. - tool build ? ...? ?-test 0|1? + tool build ?-test 0|1? ? ...? Resolve a zig satisfying the tool's declared floor, run the 'zig build test' gate (default on; the gate is the EXIT CODE - expected warnings on stderr do not fail it), build ReleaseSafe and install to /bin. Nothing is installed when the - gate or the build fails. With no name, all tools are + gate or the build fails. Options precede the tool names + (punk::args positional model); with no name, all tools are processed. tool test ? ...? Run each tool's 'zig build test' only. + 'make.tcl help tool ' shows a single action's usage table. + Toolchain resolution: PUNK_ZIG= overrides; otherwise /bin/tools/zig* is scanned and the LOWEST release satisfying the floor wins (deterministic as @@ -3404,7 +3416,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bakehouse]}" -& -help -& {${[dict get $HELPTEXTS bakehouse]}} - @form -synopsis "make.tcl bakehouse ?-k? ?-dirty-abort 1|0? ?-confirm 0|1?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT_ON} @@ -3417,7 +3428,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES packages]}" -& -help -& {${[dict get $HELPTEXTS packages]}} - @form -synopsis "make.tcl packages ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3429,7 +3439,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES modules]}" -& -help -& {${[dict get $HELPTEXTS modules]}} - @form -synopsis "make.tcl modules ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3441,7 +3450,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES libs]}" -& -help -& {${[dict get $HELPTEXTS libs]}} - @form -synopsis "make.tcl libs ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3453,7 +3461,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bake]}" -& -help -& {${[dict get $HELPTEXTS bake]}} - @form -synopsis "make.tcl bake ?-k? ?-dirty-abort? ?-confirm 0|1? ?kitname ...?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT} @@ -3472,7 +3479,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bakelist]}" -& -help -& {${[dict get $HELPTEXTS bakelist]}} - @form -synopsis "make.tcl bakelist ?kitname ...?" @values -min 0 -max -1 kitname -type string -optional 1 -multiple 1${$KITNAME_CHOICEPART} -help -& "Configured kit output(s) to report on - a kit name, or @ @@ -3486,7 +3492,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vfslibs]}" -& -help -& {${[dict get $HELPTEXTS vfslibs]}} - @form -synopsis "make.tcl vfslibs ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3498,7 +3503,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bin]}" -& -help -& {${[dict get $HELPTEXTS bin]}} - @form -synopsis "make.tcl bin ?-k? ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_FORCEKILL} ${$OPT_DIRTYABORT} @@ -3511,7 +3515,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vendorupdate]}" -& -help -& {${[dict get $HELPTEXTS vendorupdate]}} - @form -synopsis "make.tcl vendorupdate ?-confirm 0|1?" @opts ${$OPT_CONFIRM} @values -min 0 -max 0 @@ -3522,7 +3525,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES libfetch]}" -& -help -& {${[dict get $HELPTEXTS libfetch]}} - @form -synopsis "make.tcl libfetch ?-serverurl ? ?-trust-server? ?-force?" @opts ${$OPT_SERVERURL} ${$OPT_TRUSTSERVER} @@ -3535,7 +3537,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES bootsupport]}" -& -help -& {${[dict get $HELPTEXTS bootsupport]}} - @form -synopsis "make.tcl bootsupport ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3547,7 +3548,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES vfscommonupdate]}" -& -help -& {${[dict get $HELPTEXTS vfscommonupdate]}} - @form -synopsis "make.tcl vfscommonupdate ?-dirty-abort? ?-confirm 0|1?" @opts ${$OPT_DIRTYABORT} ${$OPT_CONFIRM} @@ -3559,7 +3559,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES info]}" -& -help -& {${[dict get $HELPTEXTS info]}} - @form -synopsis "make.tcl info ?-confirm 0|1?" @opts ${$OPT_CONFIRM} @values -min 0 -max 0 @@ -3570,7 +3569,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES check]}" -& -help -& {${[dict get $HELPTEXTS check]}} - @form -synopsis "make.tcl check" @opts @values -min 0 -max 0 } @@ -3580,7 +3578,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES projectversion]}" -& -help -& {${[dict get $HELPTEXTS projectversion]}} - @form -synopsis "make.tcl projectversion" @opts @values -min 0 -max 0 } @@ -3590,7 +3587,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES workflow]}" -& -help -& {${[dict get $HELPTEXTS workflow]}} - @form -synopsis "make.tcl workflow" @opts @values -min 0 -max 0 } @@ -3602,7 +3598,6 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO "Run the punk shell (repl) using bootsupport libraries. All arguments after the shell subcommand are passed through to the repl untouched (they are not parsed or validated by make.tcl)." - @form -synopsis "make.tcl shell ?arg ...?" @values -min 0 -max -1 arg -type any -optional 1 -multiple 1 -help -& "Arguments passed through to the punk repl." @@ -3613,11 +3608,28 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES buildsuite]}" -& -help -& {${[dict get $HELPTEXTS buildsuite]}} - @form -synopsis "make.tcl buildsuite list|info|build ?? ?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." + @form -form {list info build} + @form -form list + @leaders -min 1 -max 1 + action -choices {list} -choicelabels -& + {list "Discover the suites (directories carrying a suite.tcl driver) with their one-line descriptions"} + @values -min 0 -max 0 + @form -form info + @leaders -min 1 -max 1 + action -choices {info} -choicelabels -& + {info "Show a suite's configured detail: description, source records, zig pin, products, doc pointers"} + @values -min 1 -max 1 + suitename -type string -optional 0 -help -& + "Suite name (a directory under src/buildsuites carrying a suite.tcl driver)." + @form -form build + @leaders -min 1 -max 1 + action -choices {build} -choicelabels -& + {build "Run the suite's driver ('suite.tcl build'), forwarding trailing args untouched"} + @values -min 1 -max -1 + suitename -type string -optional 0 -help -& + "Suite to build." + driverarg -type any -optional 1 -multiple 1 -help -& + "Driver arguments forwarded to suite.tcl untouched (e.g -tclbranch/-refresh/-steps)." } punk::args::define { @@ -3626,11 +3638,38 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES tool]}" -& -help -& {${[dict get $HELPTEXTS tool]}} - @form -synopsis "make.tcl tool list|info|build|test ? ...? ?-test 0|1?" + @form -form {list info build test} + @form -form list + @leaders -min 1 -max 1 + action -choices {list} -choicelabels -& + {list "Discover the tools with version, zig floor, install state and vendored commit; report the resolved toolchain"} @values -min 0 -max -1 - arg -type any -optional 1 -multiple 1 -help -& - "Action (list|info|build|test), tool name(s), and - for build - - the -test 0|1 gate flag. Parsed by the handler." + toolname -type string -optional 1 -multiple 1 -& + -help "Validated against the configured tools (list reports all regardless)." + @form -form info + @leaders -min 1 -max 1 + action -choices {info} -choicelabels -& + {info "Per-tool detail: paths, provenance records, toolchain resolution"} + @values -min 1 -max -1 + toolname -type string -optional 0 -multiple 1 -& + -help "Tool(s) to describe (directory names under src/tools)." + @form -form build + @leaders -min 1 -max 1 + action -choices {build} -choicelabels -& + {build "Resolve zig, run the test gate, build ReleaseSafe and install to /bin"} + @opts + -test -type boolean -default 1 -help -& + "Run the 'zig build test' gate before building (default 1). The gate is the exit code." + @values -min 0 -max -1 + toolname -type string -optional 1 -multiple 1 -& + -help "Tool(s) to build; with no name, all tools are processed." + @form -form test + @leaders -min 1 -max 1 + action -choices {test} -choicelabels -& + {test "Run each tool's 'zig build test' only"} + @values -min 0 -max -1 + toolname -type string -optional 1 -multiple 1 -& + -help "Tool(s) to test; with no name, all tools are processed." } punk::args::define { @@ -3639,11 +3678,13 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO -summary "${[dict get $SUMMARIES help]}" -& -help -& {${[dict get $HELPTEXTS help]}} - @form -synopsis "make.tcl help ?subcommand?" - @values -min 1 -max 1 + @values -min 1 -max 2 subject -type string -optional 0 -& -choices {${[dict keys $SUMMARIES]}} -& -help "Subcommand to show usage for." + action -type string -optional 1 -help -& + "Action word (or 0-based form index) of a multi-form subcommand + (tool, buildsuite) - shows that action's single-form usage." } #top-level definition: the subcommand table rendered by 'make.tcl' / 'make.tcl -help' variable SUBGROUPS { @@ -3664,16 +3705,21 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO 'bin' and 'modules' folders at the same level as 'src'. General usage: make.tcl ?flags? - 'make.tcl help ' or 'make.tcl -help' shows a - subcommand's own usage. Interactive y/n confirmations can be driven - non-interactively with the -confirm flag declared on the relevant - subcommands.} + 'make.tcl help ?action?' or 'make.tcl ?action? -help' + shows a subcommand's own usage (a single action's usage for the + multi-form tool/buildsuite subcommands). Interactive y/n + confirmations can be driven non-interactively with the -confirm + flag declared on the relevant subcommands.} punk::args::define { @id -id "(script)::punkboot" @cmd -name "make.tcl" -& -summary "punkshell project build tool (punk boot)" -& -help -& {${$TOPLEVEL_HELP}} + #sole remaining explicit synopsis override (G-143 retired the per-subcommand + #ones per G-144's @cmd -name auto-synopsis): the auto render would show the + #subcommand leader as mandatory - this line states the bare-invocation and + #per-subcommand-flags reality the declaration cannot. @form -synopsis "make.tcl ?subcommand? ?flags?" @leaders -min 1 -max 1 subcommand -type string -optional 0 -choicecolumns 1 -& @@ -3708,6 +3754,7 @@ if {$::punkboot::punkargs_ok && [package provide punk::ansi] ne "" && [package p set do_help 0 set help_subject "" +set help_action "" set help_exitcode 0 #defaults for the option-derived variables (fallback scan and passthrough subcommands rely on these) set ::punkboot::opt_forcekill 0 @@ -3743,10 +3790,26 @@ if {$::punkboot::punkargs_ok} { exit 1 } set help_subject [dict get $argd values subject] + if {[dict exists $argd values action]} { + #G-143: 'make.tcl help ' renders the action's + #single-form usage (resolved against the subject's forms below) + set help_action [dict get $argd values action] + } } } elseif {$wants_help} { set do_help 1 set help_subject $subcommand + #G-143: ' -help' routes to the action's single-form + #usage when the subcommand is declared multi-form (tool, buildsuite) - + #the action candidate is the first non-flag word. Single-form subcommands + #keep the historic whole-subcommand render (trailing words ignored). + set firstword "" + foreach w $subargs { + if {![string match -* $w]} {set firstword $w ; break} + } + if {$firstword ne "" && [llength [punk::args::forms (script)::punkboot::$subcommand]] > 1} { + set help_action $firstword + } } elseif {$subcommand eq "shell"} { #declared passthrough: everything after 'shell' goes to the repl unparsed set ::punkboot::command shell @@ -3756,9 +3819,29 @@ if {$::punkboot::punkargs_ok} { set ::punkboot::command buildsuite set ::punkboot::bs_args $subargs } elseif {$subcommand eq "tool"} { - #declared passthrough: the handler parses action/names/-test itself (G-126) + #G-143: tool is declared multi-form (one @form per action) and dispatch + #parses through the definition - unknown actions and unknown flags in + #option position produce pointed punk::args usage errors (exit 1). The + #definition's positional model puts -test before the tool names + #('make.tcl tool build -test 0 ...'); the historic flag-anywhere + #order is not carried over (docs updated). Bare 'tool' selects the list + #form. The degraded plain scan keeps the historic manual tail parse + #(handler-side, exit-2 surface preserved there). + if {![llength $subargs]} {set subargs [list list]} + if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::tool} argd]} { + puts stderr $argd + exit 1 + } set ::punkboot::command tool - set ::punkboot::tool_args $subargs + set ::punkboot::tool_action [dict get $argd form] + set ::punkboot::tool_names [list] + if {[dict exists $argd values toolname]} { + set ::punkboot::tool_names [dict get $argd values toolname] + } + set ::punkboot::tool_dotest 1 + if {[dict exists $argd opts -test]} { + set ::punkboot::tool_dotest [dict get $argd opts -test] + } } else { if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::$subcommand} argd]} { puts stderr $argd @@ -3871,7 +3954,32 @@ if {$do_help} { if {$help_subject ne ""} { set usage_id (script)::punkboot::$help_subject } - if {![catch {punk::args::usage $usage_id} usage_out]} { + set usage_args [list] + if {$help_action ne ""} { + #G-143: per-action single-form usage. An unknown action word is a pointed + #punk::args usage error (exit 1) - never a plain-help fallback. Form + #names resolve by unambiguous prefix, matching the dispatch convention; + #a 0-based form INDEX is also accepted - the notation punk::args itself + #advertises (the ' i -form N ...' synopsis hints and its -form error + #messages both speak indexes). + set form_names [punk::args::forms $usage_id] + set fmatch [tcl::prefix::match -error "" $form_names $help_action] + if {$fmatch eq "" && [string is integer -strict $help_action] + && $help_action >= 0 && $help_action < [llength $form_names]} { + set fmatch [lindex $form_names $help_action] + } + if {$fmatch eq ""} { + if {[catch {punk::args::usage -form $help_action $usage_id} usage_err]} { + puts stderr $usage_err + } else { + puts stderr "make.tcl: no action '$help_action' for '$help_subject' (actions: $form_names)" + } + exit 1 + } + set help_action $fmatch + lappend usage_args -form $help_action + } + if {![catch {punk::args::usage {*}$usage_args $usage_id} usage_out]} { puts stdout $usage_out set availability_note [::punkboot::punkboot_availability_note] if {$availability_note ne ""} { @@ -5126,30 +5234,40 @@ if {$::punkboot::command eq "tool"} { #fetch guidance. Build caches (.zig-cache/zig-out) are deliberately left in #place between builds for rebuild speed (both git- and fossil-ignored). set tool_root [file join $sourcefolder tools] - if {![info exists ::punkboot::tool_args]} {set ::punkboot::tool_args [list]} - set targs $::punkboot::tool_args - set tool_action [lindex $targs 0] - if {$tool_action eq ""} {set tool_action list} - #args after the action: tool names plus an optional '-test 0|1' flag (build) - set tool_dotest 1 - set tool_names [list] - set _i 1 - while {$_i < [llength $targs]} { - set a [lindex $targs $_i] - if {$a eq "-test"} { - incr _i - set v [lindex $targs $_i] - if {![string is boolean -strict $v]} { - puts stderr "make.tcl tool: -test requires a boolean value (got '$v')" - exit 2 + if {$::punkboot::punkargs_ok} { + #G-143: action/names/-test already parsed through the multi-form definition + #at dispatch (pointed punk::args usage errors for unknown actions/flags). + set tool_action $::punkboot::tool_action + set tool_names $::punkboot::tool_names + set tool_dotest $::punkboot::tool_dotest + } else { + #degraded plain scan: the handler's historic manual tail parse (its unknown + #action / bad -test exit-2 surface is preserved in this mode) + if {![info exists ::punkboot::tool_args]} {set ::punkboot::tool_args [list]} + set targs $::punkboot::tool_args + set tool_action [lindex $targs 0] + if {$tool_action eq ""} {set tool_action list} + #args after the action: tool names plus an optional '-test 0|1' flag (build) + set tool_dotest 1 + set tool_names [list] + set _i 1 + while {$_i < [llength $targs]} { + set a [lindex $targs $_i] + if {$a eq "-test"} { + incr _i + set v [lindex $targs $_i] + if {![string is boolean -strict $v]} { + puts stderr "make.tcl tool: -test requires a boolean value (got '$v')" + exit 2 + } + set tool_dotest [expr {bool($v)}] + } else { + lappend tool_names $a } - set tool_dotest [expr {bool($v)}] - } else { - lappend tool_names $a + incr _i } - incr _i + unset -nocomplain _i a v } - unset -nocomplain _i a v proc ::punkboot::tool_dirs {tool_root} { set dirs [list] @@ -5297,6 +5415,9 @@ if {$::punkboot::command eq "tool"} { set selected [list] foreach n $tool_names { if {![dict exists $byname $n]} { + if {[string match -* $n]} { + puts stderr "tool: '$n' looks like a flag - options precede the tool names (e.g 'make.tcl tool build -test 0 ...')" + } puts stderr "tool '$n' not found under $tool_root - configured tools: [dict keys $byname]" exit 2 } @@ -5340,7 +5461,7 @@ if {$::punkboot::command eq "tool"} { } puts stdout "" puts stdout " - 'make.tcl tool info ' shows a tool's detail" - puts stdout " - 'make.tcl tool build ? ...? ?-test 0|1?' builds (test-gated) and installs to bin/" + puts stdout " - 'make.tcl tool build ?-test 0|1? ? ...?' builds (test-gated) and installs to bin/" puts stdout " - zig is OPTIONAL for punkshell builds: packages/bake never require this step" exit 0 } @@ -5446,7 +5567,7 @@ if {$::punkboot::command eq "tool"} { } default { puts stderr "unknown tool action '$tool_action' - expected list|info|build|test" - puts stderr "usage: make.tcl tool list|info|build|test ? ...? ?-test 0|1?" + puts stderr "usage: make.tcl tool list|info|build|test ?-test 0|1? ? ...?" exit 2 } }