diff --git a/src/bootsupport/modules/punk/mix/templates-0.2.0.tm b/src/bootsupport/modules/punk/mix/templates-0.2.0.tm index 13fb1158..5ce31b67 100644 Binary files a/src/bootsupport/modules/punk/mix/templates-0.2.0.tm and b/src/bootsupport/modules/punk/mix/templates-0.2.0.tm differ diff --git a/src/bootsupport/modules/punkboot/utils-0.6.2.tm b/src/bootsupport/modules/punkboot/utils-0.7.0.tm similarity index 88% rename from src/bootsupport/modules/punkboot/utils-0.6.2.tm rename to src/bootsupport/modules/punkboot/utils-0.7.0.tm index da683f94..adbb96d4 100644 --- a/src/bootsupport/modules/punkboot/utils-0.6.2.tm +++ b/src/bootsupport/modules/punkboot/utils-0.7.0.tm @@ -7,7 +7,7 @@ # (C) 2023 # # @@ Meta Begin -# Application punkboot::utils 0.6.2 +# Application punkboot::utils 0.7.0 # Meta platform tcl # Meta license BSD # @@ Meta End @@ -23,7 +23,7 @@ package require Tcl 8.6- # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punkboot::utils { variable version 0.1.0 - namespace export binary_arch_classify kit_offsetstyle_report parse_punkproject_version read_punkproject_version read_changelog_latest_version vcs_dirty_warnings vfs_binary_arch_report vfs_boot_library_report + namespace export binary_arch_classify kit_offsetstyle_report parse_punkproject_version read_punkproject_version read_changelog_latest_version vcs_dirty_warnings vfs_binary_arch_report vfs_boot_library_report vfs_startup_script_report namespace eval argdoc { @@ -336,6 +336,92 @@ namespace eval punkboot::utils { return $report } + + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punkboot::utils::vfs_startup_script_report + @cmd -name "::punkboot::utils::vfs_startup_script_report"\ + -summary\ + "Report what supplies a kit source .vfs folder's root startup script"\ + -help\ + "Structural census of the root-level startup script of a kit SOURCE + .vfs folder (pre-merge): an actual main.tcl file, root fauxlinks + whose resolved nominal name is main.tcl (target existence is not + examined - presence of the link is what matters), or neither. + Reads directory names only - nothing is executed and no fauxlink + target is opened. + + ok is 0 only for the G-031 startup-script collision: more than one + root-level supplier of the name main.tcl (an actual main.tcl file + plus one or more fauxlinks resolving to that name, or several such + fauxlinks). A merge materialises whichever supplier it meets last, + so a collision lets traversal order silently decide the kit's boot + script - make.tcl's bake refuses such a kit rather than build an + ambiguous artifact. A folder with NO startup script is ok here + (legal bare kit - the bake's separate missing-startup warning owns + that case), as is a nested fauxlink such as app/main.tcl (the + undroidwish convention, G-129) - root level only. + + Returned dict keys: + ok 0 only on collision (or unreadable folder) + status one of: main fauxlink none collision unchecked + mainfile main.tcl when the actual file is present, else \"\" + mainlinks root fauxlink filenames resolving to main.tcl + unresolvable root fauxlink filenames fauxlink::resolve rejects + reason why ok is 0 (or unchecked); empty string when clean" + @leaders + vfsfolder -type string -optional 0 -help\ + "Path of the kit source .vfs folder to inspect" + }] + } + proc vfs_startup_script_report {vfsfolder} { + set report [dict create ok 1 status none mainfile "" mainlinks {} unresolvable {} reason ""] + if {![file isdirectory $vfsfolder]} { + dict set report ok 0 + dict set report reason "no such directory: $vfsfolder" + return $report + } + if {[catch {package require fauxlink} errM]} { + #cannot examine links - report unchecked rather than guessing (callers + #degrade to a notice; the gate must not refuse kits it could not check) + dict set report status unchecked + dict set report reason "cannot check: fauxlink package unavailable ($errM)" + return $report + } + if {[file isfile [file join $vfsfolder main.tcl]]} { + dict set report mainfile main.tcl + } + set mainlinks [list] + set unresolvable [list] + foreach link [lsort [glob -nocomplain -dir $vfsfolder -types f *.fxlnk *.fauxlink]] { + if {[catch {fauxlink::resolve $link} linkinfo]} { + lappend unresolvable [file tail $link] + continue + } + #resolved nominal name - covers empty-nominalname links whose effective + #name is the target's tail (filename-prefix parsing would miss them) + if {[dict get $linkinfo name] eq "main.tcl"} { + lappend mainlinks [file tail $link] + } + } + dict set report mainlinks $mainlinks + dict set report unresolvable $unresolvable + set suppliers $mainlinks + if {[dict get $report mainfile] ne ""} { + set suppliers [linsert $mainlinks 0 main.tcl] + } + if {[llength $suppliers] > 1} { + dict set report ok 0 + dict set report status collision + dict set report reason "[llength $suppliers] root-level suppliers of the startup-script name main.tcl coexist in [file tail $vfsfolder]: [join $suppliers { AND }] - a merge materialises whichever it meets last, so traversal order silently decides the kit's boot script. Keep exactly one (the fauxlink into _config/ is the punkshell convention)" + } elseif {[dict get $report mainfile] ne ""} { + dict set report status main + } elseif {[llength $mainlinks]} { + dict set report status fauxlink + } + return $report + } + #G-133 binary-arch scan support. Recognition data is deliberately plain namespace #variables so extending it (a new vendor spelling, a new cpu token) is a visible #one-line edit rather than a code change. @@ -805,8 +891,8 @@ namespace eval ::punk::args::register { ## Ready package provide punkboot::utils [tcl::namespace::eval punkboot::utils { variable version - #- this version number, exactly 0.6.2, is a literal used in src module folders + #- this version number, exactly 0.7.0, is a literal used in src module folders #- we refer to this sometimes as the magic version number - set version 0.6.2 + set version 0.7.0 }] return 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 581d3700..ddf63955 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 @@ -4435,6 +4435,20 @@ proc ::punkboot::get_vfs_boot_library_report {vfsfolder} { } return [list 1 [::punkboot::utils::vfs_boot_library_report $vfsfolder]] } +#Availability probe + root startup-script census of a kit SOURCE .vfs folder (G-031 +#startup-script collision gate), consumed by the bake loop's per-kit refusal. Same +#guarded-require treatment as the boot-precondition check above: a stale bootsupport +#snapshot degrades the gate to a NOTE. Returns {available report}. +proc ::punkboot::get_vfs_startup_script_report {vfsfolder} { + set available [expr {\ + ![catch {package require punkboot::utils}]\ + && [llength [info commands ::punkboot::utils::vfs_startup_script_report]]\ + }] + if {!$available} { + return [list 0 [dict create]] + } + return [list 1 [::punkboot::utils::vfs_startup_script_report $vfsfolder]] +} #Availability probe + advisory payload/target binary-arch scan of an assembled kit vfs #(G-133), shared by the bake loop and the 'check' command report. Same guarded-require #treatment as the boot-precondition check above: a stale bootsupport snapshot degrades @@ -7119,7 +7133,7 @@ if {$::punkboot::command ni {bakehouse bake bin}} { puts stdout " - use 'make.tcl bake' to bake executable kits/zipkits from the vfs folders if you have runtimes installed" puts stdout " Note that without the vfscommonupdate step, 'make.tcl bake' will include any manual changes in the *custom* vfs folders but" puts stdout " without the latest minted modules." - puts stdout " calling 'builtexename(.exe) dev' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" + puts stdout " calling 'builtexename(.exe) minted' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" puts stdout " - consumer full path from a clean checkout: 'make.tcl bakehouse'" puts stdout "-done-" exit 0 @@ -8421,6 +8435,8 @@ foreach vfstail $vfs_tails { #(suffixed with .exe for windows-family targets, whether or not mapvfs.config spelled it that way) puts " vfs: $vfstail runtimes to process ([llength $runtimes]): $runtimes" + set startup_collision 0 + set startup_collision_report [dict create] 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) @@ -8428,6 +8444,19 @@ foreach vfstail $vfs_tails { if {$startup_warning ne ""} { ::punkboot::print_bake_warnings [list $startup_warning] } + #G-031 startup-script collision gate verdict for this SOURCE .vfs, consumed + #per-kit below (every kit built from a colliding folder is refused; kits from + #other folders proceed). merge_over's fauxlink branch overwrites without + #warning, so a collision lets traversal order silently decide which boot + #script the built kit carries. + lassign [::punkboot::get_vfs_startup_script_report $sourcefolder/vfs/$vfstail] startup_check_available startup_collision_report + if {!$startup_check_available} { + puts stderr "NOTE: startup-script collision check unavailable (punkboot::utils vfs_startup_script_report not loadable from bootsupport) - continuing without it" + } elseif {[dict get $startup_collision_report status] eq "unchecked"} { + puts stderr "NOTE: startup-script collision check could not run ([dict get $startup_collision_report reason]) - continuing without it" + } elseif {![dict get $startup_collision_report ok]} { + set startup_collision 1 + } } #todo - non kit based - zipkit? # $runtimes may now include a dash entry "-" (from mapvfs.config file) @@ -8544,6 +8573,18 @@ foreach vfstail $vfs_tails { lappend skipped_kits [list kit $targetkit reason "deployed executable is running this build"] continue } + #G-031 startup-script collision gate: two root-level suppliers of the name + #main.tcl in the source .vfs mean the baked boot script depends on merge + #traversal order. Refuse the kit BEFORE any build product is written - as + #with the G-125 gate, a previously deployed bin/ must never be + #replaced over an ambiguity; kits from other .vfs folders proceed. + if {$startup_collision} { + set failmsg "startup-script collision in src/vfs/$vfstail - [dict get $startup_collision_report reason]. NOT BUILT and NOT DEPLOYED: any previously deployed $targetkit is untouched" + puts stderr "startup-script collision gate FAILED for kit $targetkit - refusing to bake an ambiguous boot script" + puts stderr " [dict get $startup_collision_report reason]" + lappend failed_kits [list kit $targetkit reason $failmsg] + continue + } # -- ---------- set vfs_installer [punkcheck::installtrack new $installername $basedir/.punkcheck] $vfs_installer set_source_target $sourcefolder $bakefolder @@ -9441,7 +9482,7 @@ if {$had_kits} { puts stdout " - use 'make.tcl vfscommonupdate' to promote minted modules into the base vfs folder /src/vfs/_vfscommon.vfs (promotion gate)" puts stdout " - Note that without the vfscommonupdate step, 'make.tcl bake' (the kit-assembly stage of 'make.tcl bakehouse') will bake vfs based executables" puts stdout " that include your current custom vfs folders in src/vfs, but with a _vfscommon.vfs that doesn't have the latest minted modules" - puts stdout " calling 'builtexename(.exe) dev' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" + puts stdout " calling 'builtexename(.exe) minted' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" } else { puts stdout " module builds processed" puts stdout "" diff --git a/src/project_layouts/vendor/punk/basic/src/make.tcl b/src/project_layouts/vendor/punk/basic/src/make.tcl index 581d3700..ddf63955 100644 --- a/src/project_layouts/vendor/punk/basic/src/make.tcl +++ b/src/project_layouts/vendor/punk/basic/src/make.tcl @@ -4435,6 +4435,20 @@ proc ::punkboot::get_vfs_boot_library_report {vfsfolder} { } return [list 1 [::punkboot::utils::vfs_boot_library_report $vfsfolder]] } +#Availability probe + root startup-script census of a kit SOURCE .vfs folder (G-031 +#startup-script collision gate), consumed by the bake loop's per-kit refusal. Same +#guarded-require treatment as the boot-precondition check above: a stale bootsupport +#snapshot degrades the gate to a NOTE. Returns {available report}. +proc ::punkboot::get_vfs_startup_script_report {vfsfolder} { + set available [expr {\ + ![catch {package require punkboot::utils}]\ + && [llength [info commands ::punkboot::utils::vfs_startup_script_report]]\ + }] + if {!$available} { + return [list 0 [dict create]] + } + return [list 1 [::punkboot::utils::vfs_startup_script_report $vfsfolder]] +} #Availability probe + advisory payload/target binary-arch scan of an assembled kit vfs #(G-133), shared by the bake loop and the 'check' command report. Same guarded-require #treatment as the boot-precondition check above: a stale bootsupport snapshot degrades @@ -7119,7 +7133,7 @@ if {$::punkboot::command ni {bakehouse bake bin}} { puts stdout " - use 'make.tcl bake' to bake executable kits/zipkits from the vfs folders if you have runtimes installed" puts stdout " Note that without the vfscommonupdate step, 'make.tcl bake' will include any manual changes in the *custom* vfs folders but" puts stdout " without the latest minted modules." - puts stdout " calling 'builtexename(.exe) dev' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" + puts stdout " calling 'builtexename(.exe) minted' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" puts stdout " - consumer full path from a clean checkout: 'make.tcl bakehouse'" puts stdout "-done-" exit 0 @@ -8421,6 +8435,8 @@ foreach vfstail $vfs_tails { #(suffixed with .exe for windows-family targets, whether or not mapvfs.config spelled it that way) puts " vfs: $vfstail runtimes to process ([llength $runtimes]): $runtimes" + set startup_collision 0 + set startup_collision_report [dict create] 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) @@ -8428,6 +8444,19 @@ foreach vfstail $vfs_tails { if {$startup_warning ne ""} { ::punkboot::print_bake_warnings [list $startup_warning] } + #G-031 startup-script collision gate verdict for this SOURCE .vfs, consumed + #per-kit below (every kit built from a colliding folder is refused; kits from + #other folders proceed). merge_over's fauxlink branch overwrites without + #warning, so a collision lets traversal order silently decide which boot + #script the built kit carries. + lassign [::punkboot::get_vfs_startup_script_report $sourcefolder/vfs/$vfstail] startup_check_available startup_collision_report + if {!$startup_check_available} { + puts stderr "NOTE: startup-script collision check unavailable (punkboot::utils vfs_startup_script_report not loadable from bootsupport) - continuing without it" + } elseif {[dict get $startup_collision_report status] eq "unchecked"} { + puts stderr "NOTE: startup-script collision check could not run ([dict get $startup_collision_report reason]) - continuing without it" + } elseif {![dict get $startup_collision_report ok]} { + set startup_collision 1 + } } #todo - non kit based - zipkit? # $runtimes may now include a dash entry "-" (from mapvfs.config file) @@ -8544,6 +8573,18 @@ foreach vfstail $vfs_tails { lappend skipped_kits [list kit $targetkit reason "deployed executable is running this build"] continue } + #G-031 startup-script collision gate: two root-level suppliers of the name + #main.tcl in the source .vfs mean the baked boot script depends on merge + #traversal order. Refuse the kit BEFORE any build product is written - as + #with the G-125 gate, a previously deployed bin/ must never be + #replaced over an ambiguity; kits from other .vfs folders proceed. + if {$startup_collision} { + set failmsg "startup-script collision in src/vfs/$vfstail - [dict get $startup_collision_report reason]. NOT BUILT and NOT DEPLOYED: any previously deployed $targetkit is untouched" + puts stderr "startup-script collision gate FAILED for kit $targetkit - refusing to bake an ambiguous boot script" + puts stderr " [dict get $startup_collision_report reason]" + lappend failed_kits [list kit $targetkit reason $failmsg] + continue + } # -- ---------- set vfs_installer [punkcheck::installtrack new $installername $basedir/.punkcheck] $vfs_installer set_source_target $sourcefolder $bakefolder @@ -9441,7 +9482,7 @@ if {$had_kits} { puts stdout " - use 'make.tcl vfscommonupdate' to promote minted modules into the base vfs folder /src/vfs/_vfscommon.vfs (promotion gate)" puts stdout " - Note that without the vfscommonupdate step, 'make.tcl bake' (the kit-assembly stage of 'make.tcl bakehouse') will bake vfs based executables" puts stdout " that include your current custom vfs folders in src/vfs, but with a _vfscommon.vfs that doesn't have the latest minted modules" - puts stdout " calling 'builtexename(.exe) dev' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" + puts stdout " calling 'builtexename(.exe) minted' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" } else { puts stdout " module builds processed" puts stdout "" 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 581d3700..ddf63955 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 @@ -4435,6 +4435,20 @@ proc ::punkboot::get_vfs_boot_library_report {vfsfolder} { } return [list 1 [::punkboot::utils::vfs_boot_library_report $vfsfolder]] } +#Availability probe + root startup-script census of a kit SOURCE .vfs folder (G-031 +#startup-script collision gate), consumed by the bake loop's per-kit refusal. Same +#guarded-require treatment as the boot-precondition check above: a stale bootsupport +#snapshot degrades the gate to a NOTE. Returns {available report}. +proc ::punkboot::get_vfs_startup_script_report {vfsfolder} { + set available [expr {\ + ![catch {package require punkboot::utils}]\ + && [llength [info commands ::punkboot::utils::vfs_startup_script_report]]\ + }] + if {!$available} { + return [list 0 [dict create]] + } + return [list 1 [::punkboot::utils::vfs_startup_script_report $vfsfolder]] +} #Availability probe + advisory payload/target binary-arch scan of an assembled kit vfs #(G-133), shared by the bake loop and the 'check' command report. Same guarded-require #treatment as the boot-precondition check above: a stale bootsupport snapshot degrades @@ -7119,7 +7133,7 @@ if {$::punkboot::command ni {bakehouse bake bin}} { puts stdout " - use 'make.tcl bake' to bake executable kits/zipkits from the vfs folders if you have runtimes installed" puts stdout " Note that without the vfscommonupdate step, 'make.tcl bake' will include any manual changes in the *custom* vfs folders but" puts stdout " without the latest minted modules." - puts stdout " calling 'builtexename(.exe) dev' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" + puts stdout " calling 'builtexename(.exe) minted' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" puts stdout " - consumer full path from a clean checkout: 'make.tcl bakehouse'" puts stdout "-done-" exit 0 @@ -8421,6 +8435,8 @@ foreach vfstail $vfs_tails { #(suffixed with .exe for windows-family targets, whether or not mapvfs.config spelled it that way) puts " vfs: $vfstail runtimes to process ([llength $runtimes]): $runtimes" + set startup_collision 0 + set startup_collision_report [dict create] 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) @@ -8428,6 +8444,19 @@ foreach vfstail $vfs_tails { if {$startup_warning ne ""} { ::punkboot::print_bake_warnings [list $startup_warning] } + #G-031 startup-script collision gate verdict for this SOURCE .vfs, consumed + #per-kit below (every kit built from a colliding folder is refused; kits from + #other folders proceed). merge_over's fauxlink branch overwrites without + #warning, so a collision lets traversal order silently decide which boot + #script the built kit carries. + lassign [::punkboot::get_vfs_startup_script_report $sourcefolder/vfs/$vfstail] startup_check_available startup_collision_report + if {!$startup_check_available} { + puts stderr "NOTE: startup-script collision check unavailable (punkboot::utils vfs_startup_script_report not loadable from bootsupport) - continuing without it" + } elseif {[dict get $startup_collision_report status] eq "unchecked"} { + puts stderr "NOTE: startup-script collision check could not run ([dict get $startup_collision_report reason]) - continuing without it" + } elseif {![dict get $startup_collision_report ok]} { + set startup_collision 1 + } } #todo - non kit based - zipkit? # $runtimes may now include a dash entry "-" (from mapvfs.config file) @@ -8544,6 +8573,18 @@ foreach vfstail $vfs_tails { lappend skipped_kits [list kit $targetkit reason "deployed executable is running this build"] continue } + #G-031 startup-script collision gate: two root-level suppliers of the name + #main.tcl in the source .vfs mean the baked boot script depends on merge + #traversal order. Refuse the kit BEFORE any build product is written - as + #with the G-125 gate, a previously deployed bin/ must never be + #replaced over an ambiguity; kits from other .vfs folders proceed. + if {$startup_collision} { + set failmsg "startup-script collision in src/vfs/$vfstail - [dict get $startup_collision_report reason]. NOT BUILT and NOT DEPLOYED: any previously deployed $targetkit is untouched" + puts stderr "startup-script collision gate FAILED for kit $targetkit - refusing to bake an ambiguous boot script" + puts stderr " [dict get $startup_collision_report reason]" + lappend failed_kits [list kit $targetkit reason $failmsg] + continue + } # -- ---------- set vfs_installer [punkcheck::installtrack new $installername $basedir/.punkcheck] $vfs_installer set_source_target $sourcefolder $bakefolder @@ -9441,7 +9482,7 @@ if {$had_kits} { puts stdout " - use 'make.tcl vfscommonupdate' to promote minted modules into the base vfs folder /src/vfs/_vfscommon.vfs (promotion gate)" puts stdout " - Note that without the vfscommonupdate step, 'make.tcl bake' (the kit-assembly stage of 'make.tcl bakehouse') will bake vfs based executables" puts stdout " that include your current custom vfs folders in src/vfs, but with a _vfscommon.vfs that doesn't have the latest minted modules" - puts stdout " calling 'builtexename(.exe) dev' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" + puts stdout " calling 'builtexename(.exe) minted' will allow testing of minted modules before they are baked into the kits/zipkits via 'vfscommonupdate' then 'bake'" } else { puts stdout " module builds processed" puts stdout "" diff --git a/src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm index 5f2b6921..5ce31b67 100644 Binary files a/src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm and b/src/vfs/_vfscommon.vfs/modules/punk/mix/templates-0.2.0.tm differ diff --git a/src/vfs/_vfscommon.vfs/modules/punkboot/utils-0.6.2.tm b/src/vfs/_vfscommon.vfs/modules/punkboot/utils-0.7.0.tm similarity index 88% rename from src/vfs/_vfscommon.vfs/modules/punkboot/utils-0.6.2.tm rename to src/vfs/_vfscommon.vfs/modules/punkboot/utils-0.7.0.tm index da683f94..adbb96d4 100644 --- a/src/vfs/_vfscommon.vfs/modules/punkboot/utils-0.6.2.tm +++ b/src/vfs/_vfscommon.vfs/modules/punkboot/utils-0.7.0.tm @@ -7,7 +7,7 @@ # (C) 2023 # # @@ Meta Begin -# Application punkboot::utils 0.6.2 +# Application punkboot::utils 0.7.0 # Meta platform tcl # Meta license BSD # @@ Meta End @@ -23,7 +23,7 @@ package require Tcl 8.6- # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punkboot::utils { variable version 0.1.0 - namespace export binary_arch_classify kit_offsetstyle_report parse_punkproject_version read_punkproject_version read_changelog_latest_version vcs_dirty_warnings vfs_binary_arch_report vfs_boot_library_report + namespace export binary_arch_classify kit_offsetstyle_report parse_punkproject_version read_punkproject_version read_changelog_latest_version vcs_dirty_warnings vfs_binary_arch_report vfs_boot_library_report vfs_startup_script_report namespace eval argdoc { @@ -336,6 +336,92 @@ namespace eval punkboot::utils { return $report } + + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punkboot::utils::vfs_startup_script_report + @cmd -name "::punkboot::utils::vfs_startup_script_report"\ + -summary\ + "Report what supplies a kit source .vfs folder's root startup script"\ + -help\ + "Structural census of the root-level startup script of a kit SOURCE + .vfs folder (pre-merge): an actual main.tcl file, root fauxlinks + whose resolved nominal name is main.tcl (target existence is not + examined - presence of the link is what matters), or neither. + Reads directory names only - nothing is executed and no fauxlink + target is opened. + + ok is 0 only for the G-031 startup-script collision: more than one + root-level supplier of the name main.tcl (an actual main.tcl file + plus one or more fauxlinks resolving to that name, or several such + fauxlinks). A merge materialises whichever supplier it meets last, + so a collision lets traversal order silently decide the kit's boot + script - make.tcl's bake refuses such a kit rather than build an + ambiguous artifact. A folder with NO startup script is ok here + (legal bare kit - the bake's separate missing-startup warning owns + that case), as is a nested fauxlink such as app/main.tcl (the + undroidwish convention, G-129) - root level only. + + Returned dict keys: + ok 0 only on collision (or unreadable folder) + status one of: main fauxlink none collision unchecked + mainfile main.tcl when the actual file is present, else \"\" + mainlinks root fauxlink filenames resolving to main.tcl + unresolvable root fauxlink filenames fauxlink::resolve rejects + reason why ok is 0 (or unchecked); empty string when clean" + @leaders + vfsfolder -type string -optional 0 -help\ + "Path of the kit source .vfs folder to inspect" + }] + } + proc vfs_startup_script_report {vfsfolder} { + set report [dict create ok 1 status none mainfile "" mainlinks {} unresolvable {} reason ""] + if {![file isdirectory $vfsfolder]} { + dict set report ok 0 + dict set report reason "no such directory: $vfsfolder" + return $report + } + if {[catch {package require fauxlink} errM]} { + #cannot examine links - report unchecked rather than guessing (callers + #degrade to a notice; the gate must not refuse kits it could not check) + dict set report status unchecked + dict set report reason "cannot check: fauxlink package unavailable ($errM)" + return $report + } + if {[file isfile [file join $vfsfolder main.tcl]]} { + dict set report mainfile main.tcl + } + set mainlinks [list] + set unresolvable [list] + foreach link [lsort [glob -nocomplain -dir $vfsfolder -types f *.fxlnk *.fauxlink]] { + if {[catch {fauxlink::resolve $link} linkinfo]} { + lappend unresolvable [file tail $link] + continue + } + #resolved nominal name - covers empty-nominalname links whose effective + #name is the target's tail (filename-prefix parsing would miss them) + if {[dict get $linkinfo name] eq "main.tcl"} { + lappend mainlinks [file tail $link] + } + } + dict set report mainlinks $mainlinks + dict set report unresolvable $unresolvable + set suppliers $mainlinks + if {[dict get $report mainfile] ne ""} { + set suppliers [linsert $mainlinks 0 main.tcl] + } + if {[llength $suppliers] > 1} { + dict set report ok 0 + dict set report status collision + dict set report reason "[llength $suppliers] root-level suppliers of the startup-script name main.tcl coexist in [file tail $vfsfolder]: [join $suppliers { AND }] - a merge materialises whichever it meets last, so traversal order silently decides the kit's boot script. Keep exactly one (the fauxlink into _config/ is the punkshell convention)" + } elseif {[dict get $report mainfile] ne ""} { + dict set report status main + } elseif {[llength $mainlinks]} { + dict set report status fauxlink + } + return $report + } + #G-133 binary-arch scan support. Recognition data is deliberately plain namespace #variables so extending it (a new vendor spelling, a new cpu token) is a visible #one-line edit rather than a code change. @@ -805,8 +891,8 @@ namespace eval ::punk::args::register { ## Ready package provide punkboot::utils [tcl::namespace::eval punkboot::utils { variable version - #- this version number, exactly 0.6.2, is a literal used in src module folders + #- this version number, exactly 0.7.0, is a literal used in src module folders #- we refer to this sometimes as the magic version number - set version 0.6.2 + set version 0.7.0 }] return