diff --git a/src/make.tcl b/src/make.tcl index e67c6f18..1fbcd051 100644 --- a/src/make.tcl +++ b/src/make.tcl @@ -1995,6 +1995,21 @@ proc ::punkboot::print_provenance_warnings {warninglines args} { } flush stderr } +#General build warnings that must stay noticeable despite chatty build output - same +#column-0 greppable-token + recap-at-exit treatment as provenance warnings above. +set ::punkboot::build_warnings_pending [list] +proc ::punkboot::print_build_warnings {warninglines args} { + global A + if {![array size A]} {punkboot::define_global_ansi} + foreach w $warninglines { + regsub {^WARNING: } $w "" w + puts stderr "BUILD-WARNING: $A(BAD)$w$A(RST)" + if {"-norecap" ni $args} { + lappend ::punkboot::build_warnings_pending $w + } + } + flush stderr +} #Availability probe + scoped dirty-src check, shared by the build/promotion gate below and the #'check' command report. Returns {available warninglist}. proc ::punkboot::get_src_provenance_warnings {projectroot label} { @@ -2019,6 +2034,11 @@ if {![llength [info commands ::punkboot::exit_original]]} { puts stderr "PROVENANCE-WARNING: recap of warning(s) emitted earlier in this run:" ::punkboot::print_provenance_warnings $::punkboot::provenance_warnings_pending -norecap } + if {[info exists ::punkboot::build_warnings_pending] && [llength $::punkboot::build_warnings_pending]} { + puts stderr "" + puts stderr "BUILD-WARNING: recap of warning(s) emitted earlier in this run:" + ::punkboot::print_build_warnings $::punkboot::build_warnings_pending -norecap + } ::punkboot::exit_original $returnCode } } @@ -3835,6 +3855,43 @@ proc merge_over {sourcedir targetdir {depth 0}} { $t destroy puts stdout "\n${margin}merge vfs $sourcedir over $targetdir done." } + +#Startup-script presence check for a .vfs source folder about to be built into a kit. +#A kit's boot entry is a root-level main.tcl - either an actual file, or a root-level fauxlink +#resolving to the name main.tcl whose target exists and is a .tcl file (materialised into the +#build copy by the merge_over fauxlink handling above). +#Returns an empty string when a startup script is present, otherwise warning text. +#A kit without a startup script is legal (e.g a bare runtime + payload) - callers warn and +#continue rather than aborting. +proc vfs_startup_script_warning {vfsfolder} { + if {[file isfile [file join $vfsfolder main.tcl]]} { + return "" + } + package require fauxlink + set detail "" + foreach link [glob -nocomplain -dir $vfsfolder -types f *.fxlnk *.fauxlink] { + if {[catch {fauxlink::resolve $link} linkinfo]} { + append detail "; unresolvable fauxlink: [file tail $link]" + continue + } + if {[dict get $linkinfo name] ne "main.tcl"} { + continue + } + set linktarget [dict get $linkinfo targetpath] + if {[file pathtype $linktarget] eq "relative"} { + set linktarget [file join $vfsfolder $linktarget] + } + if {[file isfile $linktarget]} { + if {[string tolower [file extension $linktarget]] eq ".tcl"} { + return "" + } + append detail "; main.tcl fauxlink target '$linktarget' is not a .tcl file" + } else { + append detail "; main.tcl fauxlink target '$linktarget' does not exist" + } + } + return "vfs folder $vfsfolder has NO STARTUP SCRIPT: no root main.tcl and no root fauxlink resolving to main.tcl with an existing .tcl target - kits built from it will have no boot script (build proceeding anyway)$detail" +} set startdir [pwd] puts stdout "Found [llength $vfs_tails] .vfs folders - checking each for executables that may need to be built" cd [file dirname $buildfolder] @@ -3904,6 +3961,14 @@ foreach vfstail $vfs_tails { #assert $runtimes is a list of executable names suffixed with .exe if on windows - whether or not specified with .exe in the mapvfs.config puts " vfs: $vfstail runtimes to process ([llength $runtimes]): $runtimes" + if {[llength $runtimes]} { + #only check startup script for .vfs folders that will actually be built against a runtime + #(e.g _vfscommon.vfs is a merge overlay with no runtime mapping and legitimately has no main.tcl) + set startup_warning [vfs_startup_script_warning $sourcefolder/vfs/$vfstail] + if {$startup_warning ne ""} { + ::punkboot::print_build_warnings [list $startup_warning] + } + } #todo - non kit based - zipkit? # $runtimes may now include a dash entry "-" (from mapvfs.config file) foreach runtime_fullname $runtimes { diff --git a/src/vfs/AGENTS.md b/src/vfs/AGENTS.md index 4b4a4580..edef1a43 100644 --- a/src/vfs/AGENTS.md +++ b/src/vfs/AGENTS.md @@ -13,6 +13,7 @@ VFS (Virtual File System) folders define the runtime payloads that get wrapped i ## Local Contracts - `*.vfs` folders are build artifacts consumed by `tclsh src/make.tcl project`. +- A `*.vfs` folder built against a runtime is expected to carry a root-level startup script: an actual `main.tcl`, or a root fauxlink resolving to the name `main.tcl` whose target exists and is a `.tcl` file. `make.tcl` warns when neither is present (column-0 `BUILD-WARNING:` token, ANSI-highlighted, recapped at end of run like provenance warnings) but still builds - a kit without a startup script is legal. Overlay-only folders with no runtime mapping (e.g `_vfscommon.vfs`) are not checked. - VFS content must be compatible with the target platform runtime. - `_vfscommon.vfs/` is an auto-generated merge of common libraries; manually editing it will be overwritten. - `_config/punk_main.tcl` accepts an optional package_mode argument before the subcommand. Valid modes are `dev`, `os`, `internal`, and `src`, dash-delimited in priority order (e.g `src-internal`). `internal` is always appended if omitted.