Browse Source

punkcheck and make.tcl fixes, project.new -update 1 support

master
Julian Noble 1 year ago
parent
commit
6b0def4446
  1. BIN
      src/_vfscommon/punk1.ico
  2. 5457
      src/bootsupport/modules/http-2.10b1.tm
  3. 2
      src/build.tcl
  4. 47
      src/make.tcl
  5. 4
      src/modules/punk-0.1.tm
  6. 4
      src/modules/punk/du-999999.0a1.0.tm
  7. 6
      src/modules/punk/mix/base-0.1.tm
  8. 6
      src/modules/punk/mix/commandset/debug-999999.0a1.0.tm
  9. 42
      src/modules/punk/mix/commandset/layout-999999.0a1.0.tm
  10. 12
      src/modules/punk/mix/commandset/module-999999.0a1.0.tm
  11. 188
      src/modules/punk/mix/commandset/project-999999.0a1.0.tm
  12. 10
      src/modules/punk/mix/commandset/scriptwrap-999999.0a1.0.tm
  13. 84
      src/modules/punk/mix/templates/.punkcheck
  14. 8
      src/modules/punk/mix/templates/layouts/project/.gitignore
  15. 0
      src/modules/punk/mix/templates/layouts/project/src/_vfscommon/lib/common_vfs_libs
  16. 5457
      src/modules/punk/mix/templates/layouts/project/src/bootsupport/modules/http-2.10b1.tm
  17. 1887
      src/modules/punk/mix/templates/layouts/project/src/bootsupport/modules/punkcheck-0.1.0.tm
  18. 5
      src/modules/punk/mix/templates/layouts/project/src/build.tcl
  19. 566
      src/modules/punk/mix/templates/layouts/project/src/make.tcl
  20. 47
      src/modules/punk/mix/templates/layouts/project/src/mixtemplates/layouts/basic/.gitignore
  21. 13
      src/modules/punk/mix/templates/layouts/project/src/mixtemplates/layouts/basic/README.md
  22. 11
      src/modules/punk/mix/templates/layouts/project/src/mixtemplates/layouts/basic/src/modules/README.md
  23. 1
      src/modules/punk/mix/templates/layouts/project/src/mixtemplates/modules/template_module-0.0.1.tm
  24. 1
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sample/pkgIndex.tcl
  25. 8
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sample/sample.tcl
  26. 2
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sampleshell/pkgIndex.tcl
  27. 111
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sampleshell/repl.tcl
  28. 23
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/main.tcl
  29. 2862
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/modules/shellfilter-0.1.8.tm
  30. 710
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/modules/shellrun-0.1.tm
  31. 698
      src/modules/punk/mix/templates/layouts/project/src/sample.vfs/modules/shellthread-1.6.tm
  32. 49
      src/modules/punk/mix/templates/module/template_anyname-0.0.1.tm
  33. 0
      src/modules/punk/mix/templates/modules/modulename_buildversion.txt
  34. 0
      src/modules/punk/mix/templates/modules/modulename_description.txt
  35. 0
      src/modules/punk/mix/templates/modules/template_anyname-0.0.2.tm
  36. 0
      src/modules/punk/mix/templates/modules/template_cli-0.0.1.tm
  37. 52
      src/modules/punk/mix/templates/modules/template_module-0.0.1.tm
  38. 0
      src/modules/punk/mix/templates/modules/template_moduleexactversion-0.0.1.tm
  39. 26
      src/modules/punk/repo-999999.0a1.0.tm
  40. 70
      src/modules/punkcheck-0.1.0.tm
  41. 23
      src/punk86.vfs/lib/app-punk/repl.tcl
  42. 2
      src/runtime/mapvfs.config

BIN
src/_vfscommon/punk1.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

5457
src/bootsupport/modules/http-2.10b1.tm

File diff suppressed because it is too large Load Diff

2
src/build.tcl

@ -1,6 +1,6 @@
#!/bin/sh
# -*- tcl -*- \
# 'build.tcl' name as required by kettle
# Can be run directly - but also using `pmix Kettle ...` or `pmix KettleShell ...`
# Can be run directly - but also using `pmix Kettle ...` or `pmix KettleShell ...`\
exec ./kettle -f "$0" "${1+$@}"
kettle doc

47
src/make.tcl

@ -15,7 +15,7 @@ namespace eval ::punkmake {
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k]
variable help_flags [list -help --help /?]
variable known_commands [list project get-project-info]
variable known_commands [list project get-project-info shell bootsupport]
}
if {"::try" ni [info commands ::try]} {
puts stderr "Tcl interpreter possibly too old - 'try' command not found - aborting"
@ -251,11 +251,22 @@ if {$::punkmake::command eq "get-project-info"} {
}
if {$::punkmake::command eq "shell"} {
#package require pu
package require punk
package require punk::repl
puts stderr "make shell not fully implemented - dropping into ordinary punk shell"
repl::start stdin
exit 1
}
if {$::punkmake::command eq "bootsupport"} {
exit 1
}
if {$::punkmake::command ne "project"} {
puts stderr "Command $::punkmake::command not implemented - aborting."
exit 1
@ -270,7 +281,11 @@ file mkdir $target_modules_base
#external libs and modules first - and any supporting files - no 'building' required
if {[file exists $sourcefolder/vendorlib]} {
#unpublish README.md from source folder - but on the root one
#unpublish README.md from source folder - but only the root one
#-unpublish_paths takes relative patterns e.g
# */test.txt will only match test.txt exactly one level deep.
# */*/*.foo will match any path ending in .foo that is exactly 2 levels deep.
# **/test.txt will match at any level below the root (but not in the root)
set unpublish [list\
README.md\
]
@ -279,7 +294,8 @@ if {[file exists $sourcefolder/vendorlib]} {
set copied [dict get $resultdict files_copied]
set sources_unchanged [dict get $resultdict sources_unchanged]
puts stdout "--------------------------"
puts stderr "Copied [llength $copied] vendor libs from src/vendorlib to $projectroot/lib"
flush stdout
puts stderr "Copied [llength $copied] vendor lib files from src/vendorlib to $projectroot/lib"
foreach f $copied {
puts stdout "COPIED $f"
}
@ -296,7 +312,8 @@ if {[file exists $sourcefolder/vendormodules]} {
set copied [dict get $resultdict files_copied]
set sources_unchanged [dict get $resultdict sources_unchanged]
puts stdout "--------------------------"
puts stderr "Copied [llength $copied] vendor modules from src/vendormodules to $target_modules_base"
flush stdout
puts stderr "Copied [llength $copied] vendor module files from src/vendormodules to $target_modules_base"
foreach f $copied {
puts stdout "COPIED $f"
}
@ -311,7 +328,16 @@ if {[file exists $sourcefolder/vendormodules]} {
#e.g The default project layout is mainly folder structure and readme files - but has some scripts developed under the main src that we want to sync
#src to src/modules/punk/mix/templates/layouts/project/src
set templatebase $sourcefolder/modules/punk/mix/templates
set layout_update_list [list\
[list project $sourcefolder/modules/punk/mix/templates]\
[list basic $sourcefolder/mixtemplates]\
]
foreach layoutinfo $layout_update_list {
lassign $layoutinfo layout templatebase
if {![file exists $templatebase]} {
continue
}
set config [dict create\
-make-step sync_templates\
]
@ -322,8 +348,8 @@ if {[file exists $sourcefolder/vendormodules]} {
#----------
set pairs [list]
set pairs [list\
[list $sourcefolder/build.tcl $templatebase/layouts/project/src/build.tcl]\
[list $sourcefolder/make.tcl $templatebase/layouts/project/src/make.tcl]\
[list $sourcefolder/build.tcl $templatebase/layouts/$layout/src/build.tcl]\
[list $sourcefolder/make.tcl $templatebase/layouts/$layout/src/make.tcl]\
]
foreach filepair $pairs {
@ -356,6 +382,7 @@ if {[file exists $sourcefolder/vendormodules]} {
$tpl_event end
$tpl_event destroy
$tpl_installer destroy
}
########################################################
@ -380,8 +407,10 @@ foreach src_module_dir $source_module_folderlist {
set copied [dict get $resultdict files_copied]
set sources_unchanged [dict get $resultdict sources_unchanged]
puts stdout "--------------------------"
flush stdout
puts stderr "Copied [llength $copied] non-tm source files from $src_module_dir to $target_modules_base"
puts stderr "[llength $sources_unchanged] unchanged source files"
flush stderr
puts stdout "--------------------------"
}
@ -748,7 +777,7 @@ foreach vfs $vfs_folders {
$bin_event targetset_end OK
# -- ----------
} else {
$bin_event targetset_end FAILED
$bin_event targetset_end FAILED -note "could not delete
}
$bin_event destroy
$bin_installer destroy

4
src/modules/punk-0.1.tm

@ -5656,8 +5656,8 @@ namespace eval punk {
# and possibly containing a decimal point, i.e.: [:space:]*[:digit:]*\.[:digit:]*
proc delimit_number {unformattednumber {delim ","} {GroupSize 3}} {
set number [punk::objclone unformattednumber]
set number [string map [list _ ""] $number
set number [punk::objclone $unformattednumber]
set number [string map [list _ ""] $number]
#normalize using expr - e.g 2e4 -> 20000.0
set number [expr {$number}]
# First, extract right hand part of number, up to and including decimal point

4
src/modules/punk/du-999999.0a1.0.tm

@ -1254,12 +1254,12 @@ namespace eval punk::du {
} else {
if {$loadstate eq "failed"} {
puts stderr "punk::du defaulting to du_dirlisting_generic because twapi load failed"
set_active_function du_dirlisting du_dirlisting_generic
punk::du::active::set_active_function du_dirlisting du_dirlisting_generic
}
tailcall du_dirlisting_generic $folderpath {*}$args
}
} else {
set_active_function du_dirlisting du_dirlisting_unix
punk::du::active::set_active_function du_dirlisting du_dirlisting_unix
tailcall du_dirlisting_unix $folderpath {*}$args
}
}

6
src/modules/punk/mix/base-0.1.tm

@ -345,7 +345,7 @@ namespace eval punk::mix::base {
}
proc mix_templates_dir {} {
puts stderr "mix_templates_dir WARNING: deprecated - use get_template_folders instead"
puts stderr "mix_templates_dir WARNING: deprecated - use get_template_basefolders instead"
set provide_statement [package ifneeded punk::mix [package require punk::mix]]
set tmdir [file dirname [lindex $provide_statement end]]
set tpldir $tmdir/mix/templates
@ -355,11 +355,11 @@ namespace eval punk::mix::base {
return $tpldir
}
#get_template_folders
#get_template_basefolders
# scriptpath - file or folder
# It represents the base point from which to search for mixtemplates folders either directly related to the scriptpath (../) or in the containing project if any
# The cwd will also be searched for project root - but with lower precedence in the resultset (later in list)
proc get_template_folders {{scriptpath ""}} {
proc get_template_basefolders {{scriptpath ""}} {
#1 lowest precedence - templates from packages (ordered by order in which packages registered with punk::cap)
set folderdict [dict create]
set template_folder_dict [punk::cap::templates::folders]

6
src/modules/punk/mix/commandset/debug-999999.0a1.0.tm

@ -37,9 +37,9 @@ namespace eval punk::mix::commandset::debug {
set modulefolders [lib::find_source_module_paths $projectdir]
puts stdout "modulefolders: $modulefolders"
set template_folder_dict [punk::mix::base::lib::get_template_folders]
puts stdout "get_template_folders output:"
pdict $template_folder_dict
set template_base_dict [punk::mix::base::lib::get_template_basefolders]
puts stdout "get_template_basefolders output:"
pdict $template_base_dict
return
}

42
src/modules/punk/mix/commandset/layout-999999.0a1.0.tm

@ -38,20 +38,20 @@ namespace eval punk::mix::commandset::layout {
return [join $templatefiles \n]
}
proc templatefiles.relative {layout} {
set template_folder_dict [punk::mix::base::lib::get_template_folders]
set template_base_dict [punk::mix::base::lib::get_template_basefolders]
set tpldirs [list]
dict for {tdir folderinfo} $template_folder_dict {
if {[file exists $tdir/layouts/$layout]} {
lappend tpldirs $tdir
set bases_containing_layout [list]
dict for {tbase folderinfo} $template_base_dict {
if {[file exists $tbase/layouts/$layout]} {
lappend bases_containing_layout $tbase
}
}
if {![llength $tpldirs]} {
if {![llength $bases_containing_layout]} {
puts stderr "Unable to locate folder for layout '$layout'"
puts stderr "searched [dict size $template_folder_dict] template folders"
puts stderr "searched [dict size $template_base_dict] template folders"
return
}
set tpldir [lindex $tpldirs end]
set tpldir [lindex $bases_containing_layout end]
set layout_base $tpldir/layouts
set layout_dir [file join $layout_base $layout]
@ -74,7 +74,7 @@ namespace eval punk::mix::commandset::layout {
}
set layouts [list]
#set tplfolderdict [punk::cap::templates::folders]
set tplfolderdict [punk::mix::base::lib::get_template_folders]
set tplfolderdict [punk::mix::base::lib::get_template_basefolders]
dict for {tdir folderinfo} $tplfolderdict {
set layout_base $tdir/layouts
#collect all layouts and use lsearch glob rather than the filesystem glob (avoid issues with dotted folder names)
@ -89,25 +89,25 @@ namespace eval punk::mix::commandset::layout {
}
namespace eval lib {
proc layout_all_files {layout} {
set tplfolderdict [punk::mix::base::lib::get_template_folders]
set tplbasedict [punk::mix::base::lib::get_template_basefolders]
set layouts_found [list]
dict for {tpldir folderinfo} $tplfolderdict {
if {[file isdirectory $tpldir/layouts/$layout]} {
lappend layouts_found $tpldir/layouts/$layout
dict for {tplbase folderinfo} $tplbasedict {
if {[file isdirectory $tplbase/layouts/$layout]} {
lappend layouts_found $tplbase/layouts/$layout
}
}
if {![llength $layouts_found]} {
puts stderr "layout '$layout' not found."
puts stderr "searched [dict size $tplfolderdict] template folders"
dict for {tpldir pkg} $tplfolderdict {
puts stderr " - $tpldir $pkg"
puts stderr "searched [dict size $tplbasedict] template folders"
dict for {tplbase pkg} $tplbasedict {
puts stderr " - $tplbase $pkg"
}
return
}
set layoutfolder [lindex $layouts_found end]
if {![file isdirectory $layoutfolder]} {
puts stderr "layout '$layout' not found in /layouts within one of template_folders. (get_template_folder returned: $tplfolderdict)"
puts stderr "layout '$layout' not found in /layouts within one of template_folders. (get_template_folder returned: $tplbasedict)"
}
set file_list [list]
util::foreach-file $layoutfolder path {
@ -121,17 +121,17 @@ namespace eval punk::mix::commandset::layout {
#todo - allow specifying which package the layout is from: e.g "punk::mix::templates project" ??
proc layout_scan_for_template_files {layout {tags {}}} {
#equivalent for projects? punk::mix::commandset::module::lib::templates_dict -scriptpath ""
set tplfolderdict [punk::cap::templates::folders]
set tplbasedict [punk::mix::base::lib::get_template_basefolders]
set layouts_found [list]
dict for {tpldir pkg} $tplfolderdict {
dict for {tpldir pkg} $tplbasedict {
if {[file isdirectory $tpldir/layouts/$layout]} {
lappend layouts_found $tpldir/layouts/$layout
}
}
if {![llength $layouts_found]} {
puts stderr "layout '$layout' not found."
puts stderr "searched [dict size $tplfolderdict] template folders"
dict for {tpldir pkg} $tplfolderdict {
puts stderr "searched [dict size $tplbasedict] template folders"
dict for {tpldir pkg} $tplbasedict {
puts stderr " - $tpldir $pkg"
}
return

12
src/modules/punk/mix/commandset/module-999999.0a1.0.tm

@ -348,17 +348,17 @@ namespace eval punk::mix::commandset::module {
set opts [dict merge $defaults $args]
set opt_scriptpath [dict get $opts -scriptpath]
set module_tfolders [list]
set tfolderdict [punk::mix::base::lib::get_template_folders $opt_scriptpath]
dict for {tdir folderinfo} $tfolderdict {
lappend module_tfolders [file join $tdir module]
set module_template_bases [list]
set tbasedict [punk::mix::base::lib::get_template_basefolders $opt_scriptpath]
dict for {tbase folderinfo} $tbasedict {
lappend module_template_bases [file join $tbase modules]
}
set template_files [list]
foreach fld $module_tfolders {
set matched_files [glob -nocomplain -dir $fld -type f template_*]
foreach basefld $module_template_bases {
set matched_files [glob -nocomplain -dir $basefld -type f template_*]
foreach tf $matched_files {
if {[string match ignore* $tf]} {
continue

188
src/modules/punk/mix/commandset/project-999999.0a1.0.tm

@ -120,8 +120,6 @@ namespace eval punk::mix::commandset::project {
return
}
}
set startdir [pwd]
if {[set in_project [punk::repo::find_project $startdir]] ne ""} {
# use this project as source of templates
@ -130,70 +128,35 @@ namespace eval punk::mix::commandset::project {
puts stdout "This project will be searched for templates"
puts stdout "-------------------------------------------"
}
#todo - detect whether inside cwd-project or inside a different project
set projectdir $projectparentdir/$projectname
if {[set target_in_project [punk::repo::find_project $projectparentdir]] ne ""} {
puts stderr "Target location for new project is already within a project: $target_in_project"
error "Nested projects not yet supported aborting"
}
set repodb_folder [punk::repo::fossil_get_repository_folder_for_project $projectname -parentfolder $startdir]
if {![string length $repodb_folder]} {
puts stderr "No usable repository database folder selected for $projectname.fossil file"
return
}
if {[file exists $repodb_folder/$projectname.fossil]} {
puts stdout "NOTICE: $repodb_folder/$projectname.fossil already exists"
if {!($opt_force || $opt_update)} {
puts stderr "-force 1 or -update 1 not specified - aborting"
return
}
}
#punk::mix::commandset::module::lib::templates_dict -scriptpath ""
set template_folder_dict [punk::mix::base::lib::get_template_folders]
set tpldirs [list]
dict for {tdir folderinfo} $template_folder_dict {
if {[file exists $tdir/layouts/$opt_layout]} {
lappend tpldirs $tdir
set template_base_dict [punk::mix::base::lib::get_template_basefolders]
set template_bases_containing_layout [list]
dict for {tbase folderinfo} $template_base_dict {
if {[file exists $tbase/layouts/$opt_layout]} {
lappend template_bases_containing_layout $tbase
}
}
if {![llength $tpldirs]} {
if {![llength $template_bases_containing_layout]} {
puts stderr "layout '$opt_layout' was not found in template dirs"
puts stderr "searched [dict size $template_folder_dict] template folders"
dict for {tdir folderinfo} $template_folder_dict {
puts stderr " - $tdir $folderinfo"
puts stderr "searched [dict size $template_base_dict] template folders"
dict for {tbase folderinfo} $template_base_dict {
puts stderr " - $tbase $folderinfo"
}
return
}
#review: silently use last entry which had the layout (?)
set tpldir [lindex $tpldirs end]
set templatebase [lindex $template_bases_containing_layout end]
if {[file exists $projectdir] && !($opt_force || $opt_update)} {
puts stderr "Unable to create new project at $projectdir - file/folder already exists use -update 1 to fill in missing items from template use -force 1 to overwrite from template"
return
} elseif {[file exists $projectdir] && $opt_force} {
puts stderr "mix new WARNING: -force 1 was supplied. Will copy layout $tpldir/layouts/$opt_layout using -force option to overwrite from template"
if {$opt_confirm ni [list 0 no false]} {
set answer [util::askuser "Do you want to proceed to possibly overwrite existing files in $projectdir? Y|N"]
if {[string tolower $answer] ne "y"} {
puts stderr "mix new aborting due to user response '$answer' (required Y|y to proceed) use -confirm 0 to avoid prompts."
return
}
}
} elseif {[file exists $projectdir] && $opt_update} {
puts stderr "mix new WARNING: -update 1 was supplied. Will copy layout $tpldir/layouts/$opt_layout using -update option to add missing items"
#todo - detect whether inside cwd-project or inside a different project
set projectdir $projectparentdir/$projectname
if {[set target_in_project [punk::repo::find_project $projectparentdir]] ne ""} {
puts stderr "Target location for new project is already within a project: $target_in_project"
error "Nested projects not yet supported aborting"
}
if {[punk::repo::is_git $projectparentdir]} {
puts stderr "mix new WARNING: target project location is within a git repo based at [punk::repo::find_git $projectparentdir]"
puts stderr "The new project will create a fossil repository (which you are free to ignore - but but will be used to confirm project base)"
@ -218,30 +181,104 @@ namespace eval punk::mix::commandset::project {
}
}
set project_dir_exists [file exists $projectdir]
if {$project_dir_exists && !($opt_force || $opt_update)} {
puts stderr "Unable to create new project at $projectdir - file/folder already exists use -update 1 to fill in missing items from template use -force 1 to overwrite from template"
return
} elseif {$project_dir_exists && $opt_force} {
puts stderr "mix new WARNING: -force 1 was supplied. Will copy layout $templatebase/layouts/$opt_layout using -force option to overwrite from template"
if {$opt_confirm ni [list 0 no false]} {
set answer [util::askuser "Do you want to proceed to possibly overwrite existing files in $projectdir? Y|N"]
if {[string tolower $answer] ne "y"} {
puts stderr "mix new aborting due to user response '$answer' (required Y|y to proceed) use -confirm 0 to avoid prompts."
return
}
}
} elseif {$project_dir_exists && $opt_update} {
puts stderr "mix new WARNING: -update 1 was supplied. Will copy layout $templatebase/layouts/$opt_layout using -update option to add missing items"
}
set fossil_repo_file ""
set is_fossil_root 0
if {$project_dir_exists && [punk::repo::is_fossil_root $projectdir]} {
set is_fossil_root 1
set fossil_repo_file [punk::repo::fossil_get_repository_file $projectdir]
if {$fossil_repo_file ne ""} {
set repodb_folder [file dirname $fossil_repo_file]
}
}
puts stdout "Initialising fossil repo: $repodb_folder/$projectname.fossil"
set fossilinit [runx -n {*}$fossil_prog init $repodb_folder/$projectname.fossil -project-name $projectname]
if {[dict get $fossilinit exitcode] != 0} {
puts stderr "fossil init failed:"
puts stderr [dict get $fossilinit stderr]
return
} else {
puts stdout "fossil init result:"
puts stdout [dict get $fossilinit stdout]
if {$fossil_repo_file eq ""} {
set repodb_folder [punk::repo::fossil_get_repository_folder_for_project $projectname -parentfolder $startdir]
if {![string length $repodb_folder]} {
puts stderr "No usable repository database folder selected for $projectname.fossil file"
return
}
}
if {[file exists $repodb_folder/$projectname.fossil]} {
puts stdout "NOTICE: $repodb_folder/$projectname.fossil already exists"
if {!($opt_force || $opt_update)} {
puts stderr "-force 1 or -update 1 not specified - aborting"
return
}
}
if {$fossil_repo_file eq ""} {
puts stdout "Initialising fossil repo: $repodb_folder/$projectname.fossil"
set fossilinit [runx -n {*}$fossil_prog init $repodb_folder/$projectname.fossil -project-name $projectname]
if {[dict get $fossilinit exitcode] != 0} {
puts stderr "fossil init failed:"
puts stderr [dict get $fossilinit stderr]
return
} else {
puts stdout "fossil init result:"
puts stdout [dict get $fossilinit stdout]
}
}
file mkdir $projectdir
set layout_dir $tpldir/layouts/$opt_layout
set layout_dir $templatebase/layouts/$opt_layout
puts stdout ">>> about to call punkcheck::install $layout_dir $projectdir"
set resultdict [dict create]
#In this case we need to override the default dir antiglob - as .fossil- folders need to be installed from template
## default_antiglob_dir_core [list "#*" "_aside" ".git" ".fossil*"]
set override_antiglob_dir_core [list #* _aside .git]
set unpublish [list\
src/doc/*\
src/doc/include/*\
]
if {$opt_force} {
punkcheck::install $layout_dir $projectdir -installer project.new -antiglob_dir_core $override_antiglob_dir_core -overwrite ALL-TARGETS
set resultdict [punkcheck::install $layout_dir $projectdir -installer project.new -antiglob_dir_core $override_antiglob_dir_core -overwrite ALL-TARGETS -unpublish_paths $unpublish]
#file copy -force $layout_dir $projectdir
} else {
punkcheck::install $layout_dir $projectdir -installer project.new -antiglob_dir_core $override_antiglob_dir_core
set resultdict [punkcheck::install $layout_dir $projectdir -installer project.new -antiglob_dir_core $override_antiglob_dir_core -overwrite installedsourcechanged-targets -unpublish_paths $unpublish]
}
if {[dict size $resultdict]} {
set copied [dict get $resultdict files_copied]
set sources_unchanged [dict get $resultdict sources_unchanged]
puts stdout "--------------------------"
flush stdout
puts stderr "Copied [llength $copied] files from $layout_dir to $projectdir"
foreach f $copied {
puts stdout "COPIED $f"
}
puts stdout "[llength $sources_unchanged] unchanged source files"
puts stdout "--------------------------"
}
set resultdict [punkcheck::install $layout_dir/src/doc $projectdir/src/doc -punkcheck_folder $projectdir -installer project.new -antiglob_dir_core $override_antiglob_dir_core -overwrite NO-TARGETS]
if {[dict size $resultdict]} {
set copied [dict get $resultdict files_copied]
set files_skipped [dict get $resultdict files_skipped]
puts stdout "--------------------------"
flush stdout
puts stderr "Copied [llength $copied] doc files from $layout_dir/src/doc to $projectdir/src/doc"
foreach f $copied {
puts stdout "COPIED $f"
}
puts stdout "[llength $files_skipped] skipped files"
puts stdout "--------------------------"
}
#lappend substfiles $projectdir/README.md
@ -260,8 +297,11 @@ namespace eval punk::mix::commandset::project {
set fpath [file join $projectdir $templatetail]
if {[file exists $fpath]} {
set fd [open $fpath r]; fconfigure $fd -translation binary; set data [read $fd]; close $fd
set data [string map [list %project% $projectname] $data]
set fdout [open $fpath w]; fconfigure $fdout -translation binary; puts -nonewline $fdout $data; close $fdout
set data2 [string map [list %project% $projectname] $data]
if {$data2 ne $data} {
puts stdout "updated template file: $fpath"
set fdout [open $fpath w]; fconfigure $fdout -translation binary; puts -nonewline $fdout $data2; close $fdout
}
} else {
puts stderr "warning: Missing template file $fpath"
}
@ -273,7 +313,13 @@ namespace eval punk::mix::commandset::project {
if {[file exists $projectdir/src/modules]} {
foreach m $opt_modules {
punk::mix::commandset::module::new $m -project $projectname -type $opt_type -force $opt_force
if {![file exists $projectdir/src/modules/$m-[punk::mix::util::magic_tm_version].tm]} {
punk::mix::commandset::module::new $m -project $projectname -type $opt_type
} else {
if {$opt_force} {
punk::mix::commandset::module::new $m -project $projectname -type $opt_type -force 1
}
}
}
} else {
puts stderr "project.new WARNING template hasn't created src/modules - skipping creation of new module(s) for project"

10
src/modules/punk/mix/commandset/scriptwrap-999999.0a1.0.tm

@ -247,9 +247,9 @@ namespace eval punk::mix::commandset::scriptwrap {
set template_folder_dict [punk::mix::base::lib::get_template_folders]
set template_base_dict [punk::mix::base::lib::get_template_basefolders]
set tpldirs [list]
dict for {tdir tsourceinfo} $template_folder_dict {
dict for {tdir tsourceinfo} $template_base_dict {
if {[file exists $tdir/utility/scriptappwrappers/$templatename]} {
lappend tpldirs $tdir
}
@ -260,7 +260,7 @@ namespace eval punk::mix::commandset::scriptwrap {
} else {
if {![llength $tpldirs]} {
set msg "No template named '$templatename' found in src/scriptapps/wrappers or in template dirs from packages"
append msg \n "Searched [dict size $template_folder_dict] template dirs"
append msg \n "Searched [dict size $template_base_dict] template dirs"
error $msg
}
@ -444,9 +444,9 @@ namespace eval punk::mix::commandset::scriptwrap {
}
}
set template_folder_dict [punk::mix::base::lib::get_template_folders]
set template_base_dict [punk::mix::base::lib::get_template_basefolders]
set tpldirs [list]
dict for {tdir tsourceinfo} $template_folder_dict {
dict for {tdir tsourceinfo} $template_base_dict {
if {[file exists $tdir/utility/scriptappwrappers]} {
lappend tpldirs $tdir
}

84
src/modules/punk/mix/templates/.punkcheck

@ -0,0 +1,84 @@
INSTALLER -tsiso 2023-11-30T01:40:19 -ts 1701268819673094 -name make.tcl -keep_events 5 {
EVENT -tsiso_begin 2023-11-30T01:40:19 -ts_begin 1701268819676147 -tsiso_end {} -ts_end {} -id 250ad5e3-c95e-4833-addf-1282d09c9fec -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-11-30T01:47:15 -ts_begin 1701269235368667 -tsiso_end {} -ts_end {} -id 473193f2-54d2-44e8-a31a-9650c20177b5 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-11-30T01:53:57 -ts_begin 1701269637315528 -tsiso_end {} -ts_end {} -id 0984f805-501d-4f53-ba65-9fd68222a994 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-11-30T01:54:41 -ts_begin 1701269681466076 -tsiso_end {} -ts_end {} -id 94ea851c-85e5-4c48-b793-37b521ecb209 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-11-30T02:00:53 -ts_begin 1701270053672048 -tsiso_end {} -ts_end {} -id 1e060522-28a2-4712-a0f9-78ecc279c4d6 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-11-30T02:01:16 -ts_begin 1701270076820494 -tsiso_end {} -ts_end {} -id 5ce76b29-2b9a-4652-8c51-4f0281752381 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-11-30T02:06:29 -ts_begin 1701270389366390 -tsiso_end {} -ts_end {} -id 5271c70f-3a87-4a53-9c46-7b064b2bd43f -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-11-30T02:16:17 -ts_begin 1701270977456325 -tsiso_end {} -ts_end {} -id c84fbf6e-7aae-44b4-9f2b-d99615b76a81 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-05T04:22:54 -ts_begin 1701710574869059 -tsiso_end {} -ts_end {} -id 08ed1a89-fbb6-4cee-a543-e7b6f69663ae -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T01:45:19 -ts_begin 1701787519119661 -tsiso_end {} -ts_end {} -id 95cbdbe1-b100-4ed6-9202-3fa1dbbe7137 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T02:32:50 -ts_begin 1701790370423077 -tsiso_end {} -ts_end {} -id 9ba7b31c-9d08-4919-b475-3683fce42a37 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T03:36:28 -ts_begin 1701794188149001 -tsiso_end {} -ts_end {} -id 52ae56d6-8032-4855-88ee-5e71801b2846 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T05:31:47 -ts_begin 1701801107537126 -tsiso_end {} -ts_end {} -id 92f7f018-6b16-469e-9336-0d4a3b9bf75a -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T05:45:26 -ts_begin 1701801926154241 -tsiso_end {} -ts_end {} -id 9aa987b8-46d5-4059-9b5f-ba1fc8e9c841 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T05:55:36 -ts_begin 1701802536235596 -tsiso_end {} -ts_end {} -id 51123563-1b90-4437-b6e6-e85b1f8b9239 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T05:58:41 -ts_begin 1701802721245826 -tsiso_end {} -ts_end {} -id d67b0687-4760-4340-8022-0ffa2e69f2b2 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
EVENT -tsiso_begin 2023-12-06T06:09:27 -ts_begin 1701803367522663 -tsiso_end {} -ts_end {} -id 35fd839e-2ef6-4391-b2ec-809149cbb0b2 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}
}
FILEINFO -targets {} -keep_installrecords 3 -keep_skipped 1 -keep_inprogress 2 {
INSTALL-FAILED -tsiso 2023-11-30T01:40:19 -ts 1701268819677101 -installer make.tcl -eventid 250ad5e3-c95e-4833-addf-1282d09c9fec -metadata_us 21289 -ts_start_transfer 1701268819698390 -transfer_us 891 -elapsed_us 22180 {
SOURCE -type file -path ../../../../build.tcl -cksum 8ab5fbcfe246195a43a7ba884d3088dbced18640 -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 9411
}
INSTALL-FAILED -tsiso 2023-11-30T01:40:19 -ts 1701268819704081 -installer make.tcl -eventid 250ad5e3-c95e-4833-addf-1282d09c9fec -metadata_us 16366 -ts_start_transfer 1701268819720447 -transfer_us 705 -elapsed_us 17071 {
SOURCE -type file -path layouts/project/src/build.tcl -cksum 5f647ac1fbff3cb74f42a48bbef5239730a90054 -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 3516
}
INSTALL-FAILED -tsiso 2023-11-30T01:40:19 -ts 1701268819725576 -installer make.tcl -eventid 250ad5e3-c95e-4833-addf-1282d09c9fec -metadata_us 21854 -ts_start_transfer 1701268819747430 -transfer_us 735 -elapsed_us 22589 {
SOURCE -type file -path ../../../../make.tcl -cksum 0e44e25f9127c61faeb1946e2f2c7adfc6cfa585 -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 10241
}
INSTALL-FAILED -tsiso 2023-11-30T01:40:19 -ts 1701268819752520 -installer make.tcl -eventid 250ad5e3-c95e-4833-addf-1282d09c9fec -metadata_us 18713 -ts_start_transfer 1701268819771233 -transfer_us 715 -elapsed_us 19428 {
SOURCE -type file -path layouts/project/src/make.tcl -cksum ca1412aac730e464406363d5fe90416cf66ce4a1 -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 5116
}
}
FILEINFO -targets layouts/project/src/build.tcl -keep_installrecords 3 -keep_skipped 1 -keep_inprogress 2 {
INSTALL-INPROGRESS -tsiso 2023-11-30T01:47:15 -ts 1701269235369501 -installer make.tcl -eventid 473193f2-54d2-44e8-a31a-9650c20177b5 -tempcontext {tag EVENT -tsiso_begin 2023-11-30T01:47:15 -ts_begin 1701269235368667 -tsiso_end {} -ts_end {} -id 473193f2-54d2-44e8-a31a-9650c20177b5 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}}
INSTALL-INPROGRESS -tsiso 2023-11-30T01:53:57 -ts 1701269637316371 -installer make.tcl -eventid 0984f805-501d-4f53-ba65-9fd68222a994 -tempcontext {tag EVENT -tsiso_begin 2023-11-30T01:53:57 -ts_begin 1701269637315528 -tsiso_end {} -ts_end {} -id 0984f805-501d-4f53-ba65-9fd68222a994 -source ../../../.. -targets . -types {} -config {-make-step sync_templates}}
INSTALL-FAILED -tsiso 2023-11-30T01:54:41 -ts 1701269681466949 -installer make.tcl -eventid 94ea851c-85e5-4c48-b793-37b521ecb209 -metadata_us 23683 -ts_start_transfer 1701269681490632 -transfer_us 2738 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/buildx.tcl": no such file or directory} -elapsed_us 26421 {
SOURCE -type missing -path ../../../../buildx.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 8987
}
INSTALL-FAILED -tsiso 2023-11-30T02:00:53 -ts 1701270053672988 -installer make.tcl -eventid 1e060522-28a2-4712-a0f9-78ecc279c4d6 -metadata_us 23887 -ts_start_transfer 1701270053696875 -transfer_us 2757 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/buildx.tcl": no such file or directory} -elapsed_us 26644 {
SOURCE -type missing -path ../../../../buildx.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 9065
}
INSTALL-FAILED -tsiso 2023-11-30T02:01:16 -ts 1701270076821516 -installer make.tcl -eventid 5ce76b29-2b9a-4652-8c51-4f0281752381 -metadata_us 24281 -ts_start_transfer 1701270076845797 -transfer_us 2813 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/buildx.tcl": no such file or directory} -elapsed_us 27094 {
SOURCE -type missing -size {} -path ../../../../buildx.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 9039
}
INSTALL-FAILED -tsiso 2023-11-30T02:06:29 -ts 1701270389367455 -installer make.tcl -eventid 5271c70f-3a87-4a53-9c46-7b064b2bd43f -metadata_us 24977 -ts_start_transfer 1701270389392432 -transfer_us 2918 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/buildx.tcl": no such file or directory} -elapsed_us 27895 {
SOURCE -type missing -size {} -path ../../../../buildx.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 9034
}
INSTALL-RECORD -tsiso 2023-11-30T02:16:17 -ts 1701270977457421 -installer make.tcl -eventid c84fbf6e-7aae-44b4-9f2b-d99615b76a81 -metadata_us 26164 -ts_start_transfer 1701270977483585 -transfer_us 3773 -note test -elapsed_us 29937 {
SOURCE -type file -size 195 -path ../../../../build.tcl -cksum 8ab5fbcfe246195a43a7ba884d3088dbced18640 -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 9681
}
INSTALL-RECORD -tsiso 2023-12-05T04:22:54 -ts 1701710574870134 -installer make.tcl -eventid 08ed1a89-fbb6-4cee-a543-e7b6f69663ae -metadata_us 25456 -ts_start_transfer 1701710574895590 -transfer_us 4425 -note test -elapsed_us 29881 {
SOURCE -type file -size 196 -path ../../../../build.tcl -cksum 54fc5a072dc4627d1df737eecc8daed2fdd17f4d -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 9776
}
INSTALL-SKIPPED -tsiso 2023-12-06T06:09:27 -ts 1701803367523924 -installer make.tcl -eventid 35fd839e-2ef6-4391-b2ec-809149cbb0b2 -elapsed_us 22312 {
SOURCE -type file -size 196 -path ../../../../build.tcl -cksum 54fc5a072dc4627d1df737eecc8daed2fdd17f4d -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 0 -metadata_us 9830
}
}
FILEINFO -targets layouts/project/src/make.tcl -keep_installrecords 3 -keep_skipped 1 -keep_inprogress 2 {
INSTALL-FAILED -tsiso 2023-11-30T01:54:41 -ts 1701269681498040 -installer make.tcl -eventid 94ea851c-85e5-4c48-b793-37b521ecb209 -metadata_us 23162 -ts_start_transfer 1701269681521202 -transfer_us 2474 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/makex.tcl": no such file or directory} -elapsed_us 25636 {
SOURCE -type missing -path ../../../../makex.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 8978
}
INSTALL-FAILED -tsiso 2023-11-30T02:00:53 -ts 1701270053704394 -installer make.tcl -eventid 1e060522-28a2-4712-a0f9-78ecc279c4d6 -metadata_us 23411 -ts_start_transfer 1701270053727805 -transfer_us 2522 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/makex.tcl": no such file or directory} -elapsed_us 25933 {
SOURCE -type missing -path ../../../../makex.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 9024
}
INSTALL-FAILED -tsiso 2023-11-30T02:01:16 -ts 1701270076853426 -installer make.tcl -eventid 5ce76b29-2b9a-4652-8c51-4f0281752381 -metadata_us 23643 -ts_start_transfer 1701270076877069 -transfer_us 2566 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/makex.tcl": no such file or directory} -elapsed_us 26209 {
SOURCE -type missing -size {} -path ../../../../makex.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 8991
}
INSTALL-FAILED -tsiso 2023-11-30T02:06:29 -ts 1701270389400265 -installer make.tcl -eventid 5271c70f-3a87-4a53-9c46-7b064b2bd43f -metadata_us 23863 -ts_start_transfer 1701270389424128 -transfer_us 2604 -note {copy failed with err: error copying "C:/repo/jn/shellspy/src/makex.tcl": no such file or directory} -elapsed_us 26467 {
SOURCE -type missing -size {} -path ../../../../makex.tcl -cksum <PATHNOTFOUND> -cksum_all_opts {-cksum_content 1 -cksum_meta auto -cksum_acls 0 -cksum_usetar auto -cksum_algorithm sha1} -changed 1 -metadata_us 9005
}
INSTALL-RECORD -tsiso 2023-12-06T01:45:19 -ts 1701787519148901 -installer make.tcl -eventid 95cbdbe1-b100-4ed6-9202-3fa1dbbe7137 -metadata_us 26024 -ts_start_transfer 1701787519174925 -transfer_us 4325 -note test -elapsed_us 30349 {
SOURCE -type file -size 32642 -path ../../../../make.tcl -cksum 80105c381fa3db05833f44b716c1536fef128d84 -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 10482
}
INSTALL-RECORD -tsiso 2023-12-06T02:32:50 -ts 1701790370452196 -installer make.tcl -eventid 9ba7b31c-9d08-4919-b475-3683fce42a37 -metadata_us 26602 -ts_start_transfer 1701790370478798 -transfer_us 4392 -note test -elapsed_us 30994 {
SOURCE -type file -size 32922 -path ../../../../make.tcl -cksum 7aea3c018ce954a67ce8254c88e07407e008247c -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 10680
}
INSTALL-RECORD -tsiso 2023-12-06T03:36:28 -ts 1701794188178099 -installer make.tcl -eventid 52ae56d6-8032-4855-88ee-5e71801b2846 -metadata_us 26790 -ts_start_transfer 1701794188204889 -transfer_us 4285 -note test -elapsed_us 31075 {
SOURCE -type file -size 32956 -path ../../../../make.tcl -cksum dda7ebdcf186a5bd8e7f9c72a8e2bc892620fcab -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 1 -metadata_us 11017
}
INSTALL-SKIPPED -tsiso 2023-12-06T06:09:27 -ts 1701803367551725 -installer make.tcl -eventid 35fd839e-2ef6-4391-b2ec-809149cbb0b2 -elapsed_us 22232 {
SOURCE -type file -size 32956 -path ../../../../make.tcl -cksum dda7ebdcf186a5bd8e7f9c72a8e2bc892620fcab -cksum_all_opts {-cksum_content 1 -cksum_meta 0 -cksum_acls 0 -cksum_usetar 0 -cksum_algorithm sha1} -changed 0 -metadata_us 10590
}
}

8
src/modules/punk/mix/templates/layouts/project/.gitignore vendored

@ -37,3 +37,11 @@ _FOSSIL_
todo.txt
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/

0
src/modules/punk/mix/templates/layouts/project/src/_vfscommon/lib/common_vfs_libs

5457
src/modules/punk/mix/templates/layouts/project/src/bootsupport/modules/http-2.10b1.tm

File diff suppressed because it is too large Load Diff

1887
src/modules/punk/mix/templates/layouts/project/src/bootsupport/modules/punkcheck-0.1.0.tm

File diff suppressed because it is too large Load Diff

5
src/modules/punk/mix/templates/layouts/project/src/build.tcl

@ -1,5 +1,6 @@
#!/bin/sh
# -*- tcl -*- \
exec kettle -f "$0" "${1+$@}"
kettle tcl
# 'build.tcl' name as required by kettle
# Can be run directly - but also using `pmix Kettle ...` or `pmix KettleShell ...`\
exec ./kettle -f "$0" "${1+$@}"
kettle doc

566
src/modules/punk/mix/templates/layouts/project/src/make.tcl

@ -15,7 +15,7 @@ namespace eval ::punkmake {
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k]
variable help_flags [list -help --help /?]
variable known_commands [list project get-project-info]
variable known_commands [list project get-project-info shell bootsupport]
}
if {"::try" ni [info commands ::try]} {
puts stderr "Tcl interpreter possibly too old - 'try' command not found - aborting"
@ -134,6 +134,7 @@ foreach pkg $::punkmake::pkg_requirements {
proc punkmake_gethelp {args} {
set scriptname [file tail [info script]]
append h "Usage:" \n
@ -250,11 +251,22 @@ if {$::punkmake::command eq "get-project-info"} {
}
if {$::punkmake::command eq "shell"} {
#package require pu
package require punk
package require punk::repl
puts stderr "make shell not fully implemented - dropping into ordinary punk shell"
repl::start stdin
exit 1
}
if {$::punkmake::command eq "bootsupport"} {
exit 1
}
if {$::punkmake::command ne "project"} {
puts stderr "Command $::punkmake::command not implemented - aborting."
exit 1
@ -269,7 +281,11 @@ file mkdir $target_modules_base
#external libs and modules first - and any supporting files - no 'building' required
if {[file exists $sourcefolder/vendorlib]} {
#unpublish README.md from source folder - but on the root one
#unpublish README.md from source folder - but only the root one
#-unpublish_paths takes relative patterns e.g
# */test.txt will only match test.txt exactly one level deep.
# */*/*.foo will match any path ending in .foo that is exactly 2 levels deep.
# **/test.txt will match at any level below the root (but not in the root)
set unpublish [list\
README.md\
]
@ -278,7 +294,8 @@ if {[file exists $sourcefolder/vendorlib]} {
set copied [dict get $resultdict files_copied]
set sources_unchanged [dict get $resultdict sources_unchanged]
puts stdout "--------------------------"
puts stderr "Copied [llength $copied] vendor libs from src/vendorlib to $projectroot/lib"
flush stdout
puts stderr "Copied [llength $copied] vendor lib files from src/vendorlib to $projectroot/lib"
foreach f $copied {
puts stdout "COPIED $f"
}
@ -295,7 +312,8 @@ if {[file exists $sourcefolder/vendormodules]} {
set copied [dict get $resultdict files_copied]
set sources_unchanged [dict get $resultdict sources_unchanged]
puts stdout "--------------------------"
puts stderr "Copied [llength $copied] vendor modules from src/vendormodules to $target_modules_base"
flush stdout
puts stderr "Copied [llength $copied] vendor module files from src/vendormodules to $target_modules_base"
foreach f $copied {
puts stdout "COPIED $f"
}
@ -305,6 +323,71 @@ if {[file exists $sourcefolder/vendormodules]} {
puts stderr "NOTE: No src/vendormodules folder found."
}
########################################################
#templates
#e.g The default project layout is mainly folder structure and readme files - but has some scripts developed under the main src that we want to sync
#src to src/modules/punk/mix/templates/layouts/project/src
set layout_update_list [list\
[list project $sourcefolder/modules/punk/mix/templates]\
[list basic $sourcefolder/mixtemplates]\
]
foreach layoutinfo $layout_update_list {
lassign $layoutinfo layout templatebase
if {![file exists $templatebase]} {
continue
}
set config [dict create\
-make-step sync_templates\
]
#----------
set tpl_installer [punkcheck::installtrack new make.tcl $templatebase/.punkcheck]
$tpl_installer set_source_target $sourcefolder $templatebase
set tpl_event [$tpl_installer start_event $config]
#----------
set pairs [list]
set pairs [list\
[list $sourcefolder/build.tcl $templatebase/layouts/$layout/src/build.tcl]\
[list $sourcefolder/make.tcl $templatebase/layouts/$layout/src/make.tcl]\
]
foreach filepair $pairs {
lassign $filepair srcfile tgtfile
#----------
$tpl_event targetset_init INSTALL $tgtfile
$tpl_event targetset_addsource $srcfile
#----------
if {\
[llength [dict get [$tpl_event targetset_source_changes] changed]]\
|| [llength [$tpl_event get_targets_exist]] < [llength [$tpl_event get_targets]]\
} {
$tpl_event targetset_started
# -- --- --- --- --- ---
puts stdout "punk module templates: Copying from $srcfile to $tgtfile"
if {[catch {
file copy -force $srcfile $tgtfile
} errM]} {
$tpl_event targetset_end FAILED -note "copy failed with err: $errM"
} else {
$tpl_event targetset_end OK -note "test"
}
# -- --- --- --- --- ---
} else {
puts stderr "."
$tpl_event targetset_end SKIPPED
}
}
$tpl_event end
$tpl_event destroy
$tpl_installer destroy
}
########################################################
#default source module folder is at projectroot/src/modules
#There may be multiple other src module folders at same level (e.g folder not being other special-purpose folder and not matching name vendor* that contains at least one .tm file in its root)
set source_module_folderlist [punk::mix::cli::lib::find_source_module_paths $projectroot]
@ -324,19 +407,15 @@ foreach src_module_dir $source_module_folderlist {
set copied [dict get $resultdict files_copied]
set sources_unchanged [dict get $resultdict sources_unchanged]
puts stdout "--------------------------"
flush stdout
puts stderr "Copied [llength $copied] non-tm source files from $src_module_dir to $target_modules_base"
puts stderr "[llength $sources_unchanged] unchanged source files"
flush stderr
puts stdout "--------------------------"
}
# ----------------------------------------
set vfs_folders [glob -nocomplain -dir $sourcefolder -types d -tail *.vfs]
if {![llength $vfs_folders]} {
puts stdout "No .vfs folders found at '$sourcefolder' - no kits to build"
puts stdout " -done- "
exit 0
}
set buildfolder [punk::mix::cli::lib::get_build_workdir $sourcefolder]
if {$buildfolder ne "$sourcefolder/_build"} {
@ -346,7 +425,7 @@ if {$buildfolder ne "$sourcefolder/_build"} {
}
#find runtime - only supports one for now.. REVIEW
#find runtimes
set rtfolder $sourcefolder/runtime
set runtimes [glob -nocomplain -dir $rtfolder -types {f x} -tail *]
if {![llength $runtimes]} {
@ -360,51 +439,133 @@ if {[catch {exec sdx help} errM]} {
exit 1
}
if {[llength $runtimes] > 1} {
puts stderr "Found multiple runtimes in $rtfolder ($runtimes) - unable to proceed - currently limited to one."
exit 3
# -- --- --- --- --- --- --- --- --- ---
#load mapvfs.config file (if any) in runtime folder to map runtimes to vfs folders.
#build a dict keyed on runtime executable name.
#If no mapfile (or no mapfile entry for that runtime) - the runtime will be paired with a matching .vfs folder in src folder. e.g punk.exe to src/punk.vfs
#If vfs folders or runtime executables which are explicitly listed in the mapfile don't exist - warn on stderr - but continue. if such nonexistants found; prompt user for whether to continue or abort.
set mapfile $rtfolder/mapvfs.config
set runtime_vfs_map [dict create]
set vfs_runtime_map [dict create]
if {[file exists $mapfile]} {
set fdmap [open $mapfile r]
fconfigure $fdmap -translation binary
set mapdata [read $fdmap]
close $fdmap
set mapdata [string map [list \r\n \n] $mapdata]
set missing [list]
foreach ln [split $mapdata \n] {
set ln [string trim $ln]
if {$ln eq "" || [string match #* $ln]} {
continue
}
set vfspaths [lassign $ln runtime]
if {[string match *.exe $runtime]} {
#.exe is superfluous but allowed
#drop windows .exe suffix so same config can work cross platform - extension will be re-added if necessary later
set runtime [string range $runtime 0 end-4]
}
set runtime_test $runtime
if {"windows" eq $::tcl_platform(platform)} {
set runtime_test $runtime.exe
}
if {![file exists [file join $rtfolder $runtime_test]]} {
puts stderr "WARNING: Missing runtime file $rtfolder/$runtime_test (line in mapvfs.config: $ln)"
lappend missing $runtime
}
foreach vfs $vfspaths {
if {![file isdirectory [file join $sourcefolder $vfs]]} {
puts stderr "WARNNING: Missing vfs folder [file join $sourcefolder $vfs] specified in mapvfs.config for runtime $runtime"
lappend missing $vfs
}
dict lappend vfs_runtime_map $vfs $runtime
}
if {[dict exists $runtime_vfs_map $runtime]} {
puts stderr "CONFIG FILE ERROR. runtime: $runtime was specified more than once in $mapfile."
exit 3
}
dict set runtime_vfs_map $runtime $vfspaths
}
if {[llength $missing]} {
puts stderr "WARNING [llength $missing] missing items from $mapfile. (TODO - prompt user to continue/abort)"
foreach m $missing {
puts stderr " $m"
}
puts stderr "continuing..."
}
}
# -- --- --- --- --- --- --- --- --- ---
set vfs_folders [glob -nocomplain -dir $sourcefolder -types d -tail *.vfs]
#add any extra .vfs folders found in runtime/mapvfs.config file (e.g myotherruntimes/something.vfs)
foreach vfs [dict keys $vfs_runtime_map] {
if {$vfs ni $vfs_folders} {
lappend vfs_folders $vfs
}
}
if {![llength $vfs_folders]} {
puts stdout "No .vfs folders found at '$sourcefolder' - no kits to build"
puts stdout " -done- "
exit 0
}
set vfs_folder_changes [dict create] ;#cache whether each .vfs folder has changes so we don't re-run tests if building from same .vfs with multiple runtime executables
set installername "make.tcl"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set runtimefile [lindex $runtimes 0]
#sdx *may* be pointed to use the runtime we use to build the kit, or the user may manually use this runtime if they don't have tclsh
#sdx will complain if the same runtime is used for the shell as is used in the -runtime argument - so we make a copy (REVIEW)
#if {![file exists $buildfolder/buildruntime.exe]} {
# file copy $rtfolder/$runtimefile $buildfolder/buildruntime.exe
#}
set basedir $buildfolder
set config [dict create\
-make-step copy_runtime\
]
lassign [punkcheck::start_installer_event $basedir/.punkcheck $installername $rtfolder $buildfolder $config] _eventid punkcheck_eventid _recordset record_list
set target_relpath [punkcheck::lib::path_relative $basedir $buildfolder/buildruntime.exe]
set file_record [punkcheck::installfile_begin $basedir $target_relpath $installername -eventid $punkcheck_eventid]
# -- --- --- --- --- ---
set source_relpath [punkcheck::lib::path_relative $basedir $rtfolder/$runtimefile]
set file_record [punkcheck::installfile_add_source_and_fetch_metadata $basedir $source_relpath $file_record]
# -- --- --- --- --- ---
set changed_unchanged [punkcheck::recordlist::file_install_record_source_changes [lindex [dict get $file_record body] end]]
if {[llength [dict get $changed_unchanged changed]]} {
set file_record [punkcheck::installfile_started_install $basedir $file_record]
# -- --- --- --- --- ---
puts stdout "Copying runtime from $rtfolder/$runtimefile to $buildfolder/buildruntime.exe"
file copy -force $rtfolder/$runtimefile $buildfolder/buildruntime.exe
# -- --- --- --- --- ---
set file_record [punkcheck::installfile_finished_install $basedir $file_record]
} else {
puts stderr "."
set file_record [punkcheck::installfile_skipped_install $basedir $file_record]
#set runtimefile [lindex $runtimes 0]
foreach runtimefile $runtimes {
#runtimefile e.g tclkit86bi.exe on windows tclkit86bi on other platforms
#sdx *may* be pointed to use the runtime we use to build the kit, or the user may manually use this runtime if they don't have tclsh
#sdx will complain if the same runtime is used for the shell as is used in the -runtime argument - so we make a copy (REVIEW)
#if {![file exists $buildfolder/buildruntime.exe]} {
# file copy $rtfolder/$runtimefile $buildfolder/buildruntime.exe
#}
set basedir $buildfolder
set config [dict create\
-make-step copy_runtime\
]
#----------
set installer [punkcheck::installtrack new $installername $basedir/.punkcheck]
$installer set_source_target $rtfolder $buildfolder
set event [$installer start_event $config]
$event targetset_init INSTALL $buildfolder/build_$runtimefile
$event targetset_addsource $rtfolder/$runtimefile
#----------
#set changed_unchanged [punkcheck::recordlist::file_install_record_source_changes [lindex [dict get $file_record body] end]]
if {\
[llength [dict get [$event targetset_source_changes] changed]]\
|| [llength [$event get_targets_exist]] < [llength [$event get_targets]]\
} {
$event targetset_started
# -- --- --- --- --- ---
puts stdout "Copying runtime from $rtfolder/$runtimefile to $buildfolder/build_$runtimefile"
if {[catch {
file copy -force $rtfolder/$runtimefile $buildfolder/build_$runtimefile
} errM]} {
$event targetset_end FAILED
} else {
$event targetset_end OK
}
# -- --- --- --- --- ---
} else {
puts stderr "."
$event targetset_end SKIPPED
}
$event end
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
#
# loop over vfs_folders and for each one, loop over configured (or matching) runtimes - build with sdx if source .vfs or source runtime exe has changed.
# we are using punkcheck to install result to buildfolder so we create a .punkcheck file at the target folder to store metadata.
# punkcheck allows us to not rely purely on timestamps (which may be unreliable)
#
set startdir [pwd]
puts stdout "Found [llength $vfs_folders] .vfs folders - building executable for each..."
cd [file dirname $buildfolder]
@ -412,8 +573,9 @@ cd [file dirname $buildfolder]
#a hash of full tree file & dir mtime may be more reasonable - but it remains to be seen if just tar & checksum is any/much slower.
#Simply rebuilding all the time may be close the speed of detecting change anyway - and almost certainly much faster when there is a change.
#Using first mtime encountered that is later than target is another option - but likely to be highly variable in speed. Last file in the tree could happen to be the latest, and this mechanism doesn't handle build on reversion to older source.
set exe_names_seen [list]
foreach vfs $vfs_folders {
set vfsname [file rootname $vfs]
puts stdout " Processing vfs $sourcefolder/$vfs"
puts stdout " ------------------------------------"
@ -423,157 +585,213 @@ foreach vfs $vfs_folders {
set config [dict create\
-make-step build_vfs\
]
lassign [punkcheck::start_installer_event $basedir/.punkcheck $installername $sourcefolder $buildfolder $config] _eventid punkcheck_eventid _recordset record_list
set target_relpath [punkcheck::lib::path_relative $basedir $buildfolder/$vfsname.exe]
set file_record [punkcheck::installfile_begin $basedir $target_relpath $installername -eventid $punkcheck_eventid]
# -- --- --- --- --- ---
set source_relpath [punkcheck::lib::path_relative $basedir $sourcefolder/$vfs]
set file_record [punkcheck::installfile_add_source_and_fetch_metadata $basedir $source_relpath $file_record]
# -- --- --- --- --- ---
set changed_unchanged [punkcheck::recordlist::file_install_record_source_changes [lindex [dict get $file_record body] end]]
if {[llength [dict get $changed_unchanged changed]]} {
set file_record [punkcheck::installfile_started_install $basedir $file_record]
# -- --- --- --- --- ---
if {[file exists $buildfolder/$vfsname]} {
puts stderr "deleting existing $buildfolder/$vfsname"
file delete $sourcefolder/_build/$vfsname
set runtimes [list]
if {[dict exists $vfs_runtime_map $vfs]} {
set runtimes [dict get $vfs_runtime_map $vfs] ;#map dict is unsuffixed (.exe stripped or was not present)
if {"windows" eq $::tcl_platform(platform)} {
set runtimes_raw $runtimes
set runtimes [list]
foreach rt $runtimes_raw {
if {![string match *.exe $rt]} {
set rt $rt.exe
}
lappend runtimes $rt
}
}
} else {
#only match this vfs to a correspondingly named runtime if there was no explicit entry for that runtime
set matchrt [file rootname [file tail $vfs]] ;#e.g project.vfs -> project
if {![dict exists $runtime_vfs_map $matchrt]} {
if {"windows" eq $::tcl_platform(platform)} {
if {[file exists $rtfolder/$matchrt.exe]} {
lappend runtimes $matchrt.exe
}
} else {
lappend runtimes $matchrt
}
}
}
#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 stdout "building $vfsname with sdx.. vfsdir:$vfs cwd: [pwd]"
foreach rtname $runtimes {
if {[catch {
exec sdx wrap $buildfolder/$vfsname -vfs $sourcefolder/$vfs -runtime $buildfolder/buildruntime.exe -verbose
} result]} {
puts stderr "sdx wrap _build/$vfsname -vfs $sourcefolder/$vfs -runtime $buildfolder/buildruntime.exe -verbose failed with msg: $result"
#first configured runtime will be the one to use the same name as .vfs folder for output executable. Additional runtimes on this .vfs will need to suffix the runtime name to disambiguate.
#review: This mechanism may not be great for multiplatform builds ? We may be better off consistently combining vfsname and rtname and letting a later platform-specific step choose ones to install in bin with simpler names.
if {$::tcl_platform(platform) eq "windows"} {
set targetexe ${vfsname}.exe
} else {
puts stdout "ok - finished sdx"
set separator [string repeat = 40]
puts stdout $separator
puts stdout $result
puts stdout $separator
set targetexe $vfsname
}
if {![file exists $buildfolder/$vfsname]} {
puts stderr "|err> build didn't seem to produce output at $sourcefolder/_build/$vfsname"
exit 2
if {$targetexe in $exe_names_seen} {
#more than one runtime for this .vfs
set targetexe ${vfsname}_$rtname
}
lappend exe_names_seen $targetexe
# -- ----------
set vfs_installer [punkcheck::installtrack new $installername $basedir/.punkcheck]
$vfs_installer set_source_target $sourcefolder $buildfolder
set vfs_event [$vfs_installer start_event {-make-step build_vfs}]
$vfs_event targetset_init INSTALL $buildfolder/$targetexe
$vfs_event targetset_addsource $sourcefolder/$vfs
$vfs_event targetset_addsource $buildfolder/build_$rtname
# -- ----------
set changed_unchanged [$vfs_event targetset_source_changes]
if {[llength [dict get $changed_unchanged changed]] || [llength [$vfs_event get_targets_exist]] < [llength [$vfs_event get_targets]]} {
#source .vfs folder has changes
$vfs_event targetset_started
# -- --- --- --- --- ---
#use
if {[file exists $buildfolder/$vfsname.new]} {
puts stderr "deleting existing $buildfolder/$vfsname.new"
file delete $buildfolder/$vfsname.new
}
puts stdout "building $vfsname with sdx.. vfsdir:$vfs cwd: [pwd]"
if {[catch {
exec sdx wrap $buildfolder/$vfsname.new -vfs $sourcefolder/$vfs -runtime $buildfolder/build_$rtname -verbose
} result]} {
puts stderr "sdx wrap $buildfolder/$vfsname.new -vfs $sourcefolder/$vfs -runtime $buildfolder/build_$rtname -verbose failed with msg: $result"
} else {
puts stdout "ok - finished sdx"
set separator [string repeat = 40]
puts stdout $separator
puts stdout $result
puts stdout $separator
}
# -- --- --- --- --- ---
set file_record [punkcheck::installfile_finished_install $basedir $file_record]
} else {
set skipped_vfs_build 1
puts stderr "."
puts stdout "Skipping build for vfs $vfs - no change detected"
set file_record [punkcheck::installfile_skipped_install $basedir $file_record]
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
if {!$skipped_vfs_build} {
if {$::tcl_platform(platform) eq "windows"} {
set pscmd "tasklist"
} else {
set pscmd "ps"
}
if {![file exists $buildfolder/$vfsname.new]} {
puts stderr "|err> make.tcl build didn't seem to produce output at $sourcefolder/_build/$vfsname.new"
$vfs_event targetset_end FAILED
exit 2
}
if {![catch {
exec $pscmd | grep $vfsname
} still_running]} {
puts stdout "found $vfsname instances still running\n"
set count_killed 0
foreach ln [split $still_running \n] {
puts stdout " $ln"
if {$::tcl_platform(platform) eq "windows"} {
set pid [lindex $ln 1]
if {$forcekill} {
set killcmd [list taskkill /F /PID $pid]
# -- --- ---
if {$::tcl_platform(platform) eq "windows"} {
set pscmd "tasklist"
} else {
set pscmd "ps"
}
if {![catch {
exec $pscmd | grep $vfsname
} still_running]} {
puts stdout "found $vfsname instances still running\n"
set count_killed 0
foreach ln [split $still_running \n] {
puts stdout " $ln"
if {$::tcl_platform(platform) eq "windows"} {
set pid [lindex $ln 1]
if {$forcekill} {
set killcmd [list taskkill /F /PID $pid]
} else {
set killcmd [list taskkill /PID $pid]
}
} else {
set killcmd [list taskkill /PID $pid]
set pid [lindex $ln 0]
#review!
if {$forcekill} {
set killcmd [list kill -9 $pid]
} else {
set killcmd [list kill $pid]
}
}
} else {
set pid [lindex $ln 0]
#review!
if {$forcekill} {
set killcmd [list kill -9 $pid]
puts stdout " pid: $pid (attempting to kill now using '$killcmd')"
if {[catch {
exec {*}$killcmd
} errMsg]} {
puts stderr "$killcmd returned an error:"
puts stderr $errMsg
puts stderr "(try '[info script] -k' option to force kill)"
exit 4
} else {
set killcmd [list kill $pid]
puts stderr "$killcmd ran without error"
incr count_killed
}
}
puts stdout " pid: $pid (attempting to kill now using '$killcmd')"
if {$count_killed > 0} {
puts stderr "\nKilled $count_killed processes. Waiting a short time before attempting to delete executable"
after 1000
}
} else {
puts stderr "Ok.. no running '$vfsname' processes found"
}
if {[file exists $buildfolder/$targetexe]} {
puts stderr "deleting existing $buildfolder/$targetexe"
if {[catch {
exec {*}$killcmd
} errMsg]} {
puts stderr "$killcmd returned an error:"
puts stderr $errMsg
puts stderr "(try '[info script] -k' option to force kill)"
file delete $buildfolder/$targetexe
} msg]} {
puts stderr "Failed to delete $buildfolder/$targetexe"
exit 4
} else {
puts stderr "$killcmd ran without error"
incr count_killed
}
}
if {$count_killed > 0} {
puts stderr "\nKilled $count_killed processes. Waiting a short time before attempting to delete executable"
after 1000
#WINDOWS filesystem 'tunneling' (file replacement within 15secs) could cause targetexe to copy ctime & shortname metadata from previous file!
#This is probably harmless - but worth being aware of.
file rename $buildfolder/$vfsname.new $buildfolder/$targetexe
# -- --- --- --- --- ---
$vfs_event targetset_end OK
after 200
set deployment_folder [file dirname $sourcefolder]/bin
file mkdir $deployment_folder
# -- ----------
set bin_installer [punkcheck::installtrack new "make.tcl" $deployment_folder/.punkcheck]
$bin_installer set_source_target $buildfolder $deployment_folder
set bin_event [$bin_installer start_event {-make-step final_exe_install}]
$bin_event targetset_init INSTALL $deployment_folder/$targetexe
$bin_event targetset_addsource $buildfolder/$targetexe
$bin_event targetset_started
# -- ----------
set delete_failed 0
if {[file exists $deployment_folder/$targetexe]} {
puts stderr "deleting existing deployed at $deployment_folder/$targetexe"
if {[catch {
file delete $deployment_folder/$targetexe
} errMsg]} {
puts stderr "deletion of deployed version at $deployment_folder/$targetexe failed: $errMsg"
#exit 5
set delete_failed 1
}
}
} else {
puts stderr "Ok.. no running '$vfsname' processes found"
}
if {$::tcl_platform(platform) eq "windows"} {
set targetexe ${vfsname}.exe
} else {
set targetexe $vfsname
}
if {[file exists $buildfolder/$targetexe]} {
puts stderr "deleting existing $buildfolder/$targetexe"
if {[catch {
file delete $sourcefolder/_build/$targetexe
} msg]} {
puts stderr "Failed to delete $buildfolder/$targetexe"
exit 4
if {!$delete_failed} {
puts stdout "copying.."
puts stdout "$buildfolder/$targetexe"
puts stdout "to:"
puts stdout "$deployment_folder/$targetexe"
after 300
file copy $buildfolder/$targetexe $deployment_folder/$targetexe
# -- ----------
$bin_event targetset_end OK
# -- ----------
} else {
$bin_event targetset_end FAILED -note "could not delete
}
}
if {$::tcl_platform(platform) eq "windows"} {
file rename $buildfolder/$vfsname $sourcefolder/_build/${vfsname}.exe
}
$bin_event destroy
$bin_installer destroy
after 200
set deployment_folder [file dirname $sourcefolder]/bin
file mkdir $deployment_folder
if {[file exists $deployment_folder/$targetexe]} {
puts stderr "deleting existing deployed at $deployment_folder/$targetexe"
if {[catch {
file delete $deployment_folder/$targetexe
} errMsg]} {
puts stderr "deletion of deployed version at $deployment_folder/$targetexe failed: $errMsg"
exit 5
}
} else {
set skipped_vfs_build 1
puts stderr "."
puts stdout "Skipping build for vfs $vfs - no change detected"
$vfs_event targetset_end SKIPPED
}
puts stdout "copying.."
puts stdout "$buildfolder/$targetexe"
puts stdout "to:"
puts stdout "$deployment_folder/$targetexe"
after 500
file copy $buildfolder/$targetexe $deployment_folder/$targetexe
}
$vfs_event destroy
$vfs_installer destroy
} ;#end foreach rtname in runtimes
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
}
cd $startdir

47
src/modules/punk/mix/templates/layouts/project/src/mixtemplates/layouts/basic/.gitignore vendored

@ -0,0 +1,47 @@
/bin/
/lib/
#The directory for compiled/built Tcl modules
/modules/
/vendorbuilds/
#Temporary files e.g from tests
/tmp/
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
#Built tclkits (if any)
punk*.exe
tcl*.exe
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#miscellaneous editor files etc
*.swp
todo.txt
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/

13
src/modules/punk/mix/templates/layouts/project/src/mixtemplates/layouts/basic/README.md

@ -0,0 +1,13 @@
%project%
==============================
+
+
About
------------------------------
+
+
+

11
src/modules/punk/mix/templates/layouts/project/src/mixtemplates/layouts/basic/src/modules/README.md

@ -0,0 +1,11 @@
Tcl Module Source files for the project.
Consider using the punkshell pmix facility to create and manage these.
pmix::newmodule <name> will create a basic .tm module template and assist in versioning.
Tcl modules can be namespaced.
For example
> pmix::newmodule mymodule::utils
will create the new module under src/modules/mymodule/utils

1
src/modules/punk/mix/templates/module/template_module-0.0.1.tm → src/modules/punk/mix/templates/layouts/project/src/mixtemplates/modules/template_module-0.0.1.tm

@ -45,6 +45,7 @@ namespace eval %pkg% {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable pkg %pkg%
variable version
set version 999999.0a1.0
}]

1
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sample/pkgIndex.tcl

@ -0,0 +1 @@
package ifneeded app-sample 0.1 [list source [file join $dir sample.tcl]]

8
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sample/sample.tcl

@ -0,0 +1,8 @@
namespace eval sample {
proc main {} {
puts stdout "[namespace current] argc $::argc argv $::argv"
puts stdout "[namespace current] done"
}
main
}
package provide app-sample 0.1

2
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sampleshell/pkgIndex.tcl

@ -0,0 +1,2 @@
package ifneeded app-sampleshell 0.1 [list source [file join $dir repl.tcl]]

111
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/lib/app-sampleshell/repl.tcl

@ -0,0 +1,111 @@
package provide app-punk 1.0
#punk linerepl launcher
#------------------------------------------------------------------------------
#Module loading
#------------------------------------------------------------------------------
#If the current directory contains .tm files when the punk repl starts - then it will attempt to preference them
# - but first add our other known relative modules paths - as it won't make sense to use current directory as a modulpath if it's an ancestor of one of these..
set original_tm_list [tcl::tm::list]
tcl::tm::remove {*}$original_tm_list
#tm list first added end up later in the list - and then override earlier ones if version the same - so add pwd-relative 1st to give higher priority
#1
if {[file exists [pwd]/modules]} {
catch {tcl::tm::add [pwd]/modules}
}
#2)
if {[string match "*.vfs/*" [info script]]} {
#src/xxx.vfs/lib/app-punk/repl.tcl
#we assume if calling directly into .vfs that the user would prefer to use src/modules - so go up 4 levels
set modulefolder [file dirname [file dirname [file dirname [file dirname [info script]]]]]/modules
} else {
# .../bin/punkXX.exe look for ../modules (i.e modules folder at same level as bin folder)
set modulefolder [file dirname [file dirname [info nameofexecutable]]]/modules
}
if {[file exists $modulefolder]} {
tcl::tm::add $modulefolder
} else {
puts stderr "Warning unable to find module folder at: $modulefolder"
}
#libs are appended to end - so add higher prioriy libraries last (opposite to modules)
#auto_path - add exe-relative after exe-relative path
set libfolder [file dirname [file dirname [info nameofexecutable]]]/lib
if {[file exists $libfolder]} {
lappend ::auto_path $libfolder
}
if {[file exists [pwd]/lib]} {
lappend ::auto_path [pwd]/lib
}
#2)
#now add current dir (if no conflict with above)
set currentdir_modules [glob -nocomplain -dir [pwd] -type f -tail *.tm]
set tcl_core_packages [list tcl::zlib zlib tcl::oo TclOO tcl::tommath tcl::zipfs Tcl Tk]
if {[llength $currentdir_modules]} {
#only forget all *unloaded* package names if we are started in a .tm containing folder
foreach pkg [package names] {
if {$pkg in $tcl_core_packages} {
continue
}
if {![llength [package versions $pkg]]} {
#puts stderr "Got no versions for pkg $pkg"
continue
}
if {![string length [package provide $pkg]]} {
package forget $pkg
}
}
catch {tcl::tm::add [pwd]}
}
#puts stdout "$::auto_path"
package require Thread
#These are strong dependencies
# - the repl requires Threading and punk,shellfilter,shellrun to call and display properly.
# tm list already indexed - need 'package forget' to find modules based on current tcl::tm::list
set required [list\
shellfilter
shellrun\
punk\
]
catch {
foreach pkg $required {
package forget $pkg
package require $pkg
}
}
#restore module paths
set tm_list_now [tcl::tm::list]
foreach p $original_tm_list {
if {$p ni $tm_list_now} {
#the prior tm paths go to the head of the list.
#They are processed first.. but an item of same version later in the list will override one at the head.
tcl::tm::add $p
}
}
#------------------------------------------------------------------------------
foreach pkg $required {
package require $pkg
}
package require punk::repl
repl::start stdin

23
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/main.tcl

@ -0,0 +1,23 @@
if {[catch {package require starkit}]} {
#presumably running the xxx.vfs/main.tcl script using a non-starkit tclsh that doesn't have starkit lib available.. lets see if we can move forward anyway
lappend ::auto_path [file join [file dirname [info script]] lib]
} else {
starkit::startup
}
#when run as a tclkit - the exe is mounted as a dir and Tcl's auto_execok doesn't find it
set thisexe [file tail [info nameofexecutable]]
set thisexeroot [file rootname $thisexe]
set ::auto_execs($thisexeroot) [info nameofexecutable]
if {$thisexe ne $thisexeroot} {
set ::auto_execs($thisexe) [info nameofexecutable]
}
if {[llength $::argv]} {
package require app-sample
} else {
package require app-sampleshell
repl::start stdin
}

2862
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/modules/shellfilter-0.1.8.tm

File diff suppressed because it is too large Load Diff

710
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/modules/shellrun-0.1.tm

@ -0,0 +1,710 @@
# vim: set ft=tcl
#
#purpose: handle the run commands that call shellfilter::run
#e.g run,runout,runerr,runx
package require shellfilter
package require punk::ansi
#NOTE: the run,runout,runerr,runx commands only produce an error if the command didn't run.
# - If it did run, but there was a non-zero exitcode it is up to the application to check that.
#This is deliberate, but means 'catch' doesn't catch errors within the command itself - the exitcode has to be checked.
#The user can always use exec for different process error semantics (they don't get exitcode with exec)
namespace eval shellrun {
variable runout
variable runerr
#do we need these?
variable punkout
variable punkerr
#some ugly coupling with punk/punk::config for now
#todo - something better
if {[info exists ::punk::config::running]} {
upvar ::punk::config::running conf
set syslog_stdout [dict get $conf syslog_stdout]
set syslog_stderr [dict get $conf syslog_stderr]
set logfile_stdout [dict get $conf logfile_stdout]
set logfile_stderr [dict get $conf logfile_stderr]
} else {
lassign [list "" "" "" ""] syslog_stdout syslog_stderr logfile_stdout logfile_stderr
}
set outdevice [shellfilter::stack::new punkout -settings [list -tag "punkout" -buffering none -raw 1 -syslog $syslog_stdout -file $logfile_stdout]]
set out [dict get $outdevice localchan]
set errdevice [shellfilter::stack::new punkerr -settings [list -tag "punkerr" -buffering none -raw 1 -syslog $syslog_stderr -file $logfile_stderr]]
set err [dict get $errdevice localchan]
namespace import ::punk::ansi::a+
namespace import ::punk::ansi::a
#repltelemetry - additional/alternative display info used in a repl context i.e info directed towards the screen
#todo - package up in repltelemetry module and rewrite proc based on whether the module was found/loaded.
#somewhat strong coupling to punk - but let's try to behave decently if it's not loaded
#The last_run_display is actually intended for the repl - but is resident in the punk namespace with a view to the possibility of a different repl being in use.
proc set_last_run_display {chunklist} {
#chunklist as understood by the
if {![info exists ::punk::repltelemetry_emmitters]} {
namespace eval ::punk {
variable repltelemetry_emmitters
set repltelemetry_emmitters "shellrun"
}
} else {
if {"shellrun" ni $::punk::repltelemetry_emmitters} {
lappend punk::repltelemetry_emmitters "shellrun"
}
}
#most basic of validity tests here.. just that it is a list (can be empty). We don't want to duplicate or over-constrain the way repls/shells/terminals interpet the info
if {[catch {llength $chunklist} errMsg]} {
error "set_last_run_display expects a list. Value supplied doesn't appear to be a well formed tcl list. '$errMsg'"
}
#todo -
set ::punk::last_run_display $chunklist
}
#maintenance: similar used in punk::ns & punk::winrun
#todo - take runopts + aliases as args
proc get_run_opts {arglist} {
if {[catch {
set callerinfo [info level -1]
} errM]} {
set caller ""
} else {
set caller [lindex $callerinfo 0]
}
#we provide -nonewline even for 'run' even though run doesn't deliver stderr or stdout to the tcl return value
#This is for compatibility with other runX commands, and the difference is also visible when calling from repl.
set known_runopts [list "-echo" "-e" "-nonewline" "-n" "-tcl"]
set aliases [list "-e" "-echo" "-echo" "-echo" "-n" "-nonewline" "-nonewline" "-nonewline" "-tcl" "-tcl"] ;#include map to self
set runopts [list]
set cmdargs [list]
set idx_first_cmdarg [lsearch -not $arglist "-*"]
set runopts [lrange $arglist 0 $idx_first_cmdarg-1]
set cmdargs [lrange $arglist $idx_first_cmdarg end]
foreach o $runopts {
if {$o ni $known_runopts} {
error "$caller: Unknown runoption $o - known options $known_runopts"
}
}
set runopts [lmap o $runopts {dict get $aliases $o}]
return [list runopts $runopts cmdargs $cmdargs]
}
proc run {args} {
set_last_run_display [list]
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
if {"-nonewline" in $runopts} {
set nonewline 1
} else {
set nonewline 0
}
set idlist_stderr [list]
#we leave stdout without imposed ansi colouring - because the source may be colourised
#stderr might have source colouring - but it usually doesn't seem to, and the visual distiction of red stderr is very handy for the run command.
#A further enhancement could be to detect well-known options such as --color and/or use a configuration for specific commands that have useful colourised stderr,
#but defaulting stderr to red is a pretty reasonable compromise.
#Note that the other run commands, runout,runerr, runx don't emit in real-time - so for those commands there may be options to detect and/or post-process stdout and stderr.
#TODO - fix. This has no effect because the repl adds an ansiwrap transform
# what we probably want to do is 'aside' that transform for runxxx commands only.
#lappend idlist_stderr [shellfilter::stack::add stderr ansiwrap -settings {-colour {red bold}}]
set callopts [dict create]
if {"-tcl" in $runopts} {
dict set callopts -tclscript 1
}
#---------------------------------------------------------------------------------------------
set exitinfo [shellfilter::run $cmdargs {*}$callopts -teehandle punk -inbuffering none -outbuffering none ]
#---------------------------------------------------------------------------------------------
foreach id $idlist_stderr {
shellfilter::stack::remove stderr $id
}
flush stderr
flush stdout
if {[dict exists $exitinfo error]} {
error "[dict get $exitinfo error]\n$exitinfo"
}
return $exitinfo
}
proc runout {args} {
set_last_run_display [list]
variable runout
variable runerr
set runout ""
set runerr ""
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
if {"-nonewline" in $runopts} {
set nonewline 1
} else {
set nonewline 0
}
#puts stdout "RUNOUT cmdargs: $cmdargs"
#todo add -data boolean and -data lastwrite to -settings with default being -data all
# because sometimes we're only interested in last char (e.g to detect something was output)
#set outvar_stackid [shellfilter::stack::add commandout tee_to_var -action float -settings {-varname ::runout}]
#
#when not echoing - use float-locked so that the repl's stack is bypassed
if {"-echo" in $runopts} {
set stdout_stackid [shellfilter::stack::add stdout tee_to_var -action float-locked -settings {-varname ::shellrun::runout}]
set stderr_stackid [shellfilter::stack::add stderr tee_to_var -action float-locked -settings {-varname ::shellrun::runerr}]
#set stderr_stackid [shellfilter::stack::add stderr tee_to_var -action sink-locked -settings {-varname ::shellrun::runerr}]
} else {
set stdout_stackid [shellfilter::stack::add stdout var -action float-locked -settings {-varname ::shellrun::runout}]
set stderr_stackid [shellfilter::stack::add stderr var -action float-locked -settings {-varname ::shellrun::runerr}]
}
set callopts ""
if {"-tcl" in $runopts} {
append callopts " -tclscript 1"
}
#shellfilter::run [lrange $args 1 end] -teehandle punk -outchan stdout -inbuffering none -outbuffering none -stdinhandler ::repl::repl_handler
set exitinfo [shellfilter::run $cmdargs {*}$callopts -teehandle punk -inbuffering none -outbuffering none ]
flush stderr
flush stdout
shellfilter::stack::remove stdout $stdout_stackid
shellfilter::stack::remove stderr $stderr_stackid
#shellfilter::stack::remove commandout $outvar_stackid
if {[dict exists $exitinfo error]} {
if {"-tcl" in $runopts} {
} else {
#we must raise an error.
#todo - check errorInfo makes sense.. return -code? tailcall?
#
set msg ""
append msg [dict get $exitinfo error]
append msg "\n(add -tcl option to run as a tcl command/script instead of an external command)"
error $msg
}
}
set chunklist [list]
#exitcode not part of return value for runout - colourcode appropriately
set n [a]
set c ""
if [dict exists $exitinfo exitcode] {
set code [dict get $exitinfo exitcode]
if {$code == 0} {
set c [a+ green]
} else {
set c [a+ white bold]
}
lappend chunklist [list "info" "$c$exitinfo$n"]
} elseif [dict exists $exitinfo error] {
set c [a+ yellow bold]
lappend chunklist [list "info" "${c}error [dict get $exitinfo error]$n"]
lappend chunklist [list "info" "errorCode [dict get $exitinfo errorCode]"]
#lappend chunklist [list "info" "errorInfo [list [dict get $exitinfo errorInfo]]"]
lappend chunklist [list "info" errorInfo]
lappend chunklist [list "stderr" [dict get $exitinfo errorInfo]]
} else {
set c [a+ Yellow red bold]
lappend chunklist [list "info" "$c$exitinfo$n"]
}
set chunk "[a+ red bold]stderr[a]"
lappend chunklist [list "info" $chunk]
set chunk ""
if {[string length $::shellrun::runerr]} {
if {$nonewline} {
set e [string trimright $::shellrun::runerr \r\n]
} else {
set e $::shellrun::runerr
}
#append chunk "[a+ red light]$e[a]\n"
append chunk "[a+ red light]$e[a]"
}
lappend chunklist [list stderr $chunk]
lappend chunklist [list "info" "[a+ white bold]stdout[a]"]
set chunk ""
if {[string length $::shellrun::runout]} {
if {$nonewline} {
set o [string trimright $::shellrun::runout \r\n]
} else {
set o $::shellrun::runout
}
append chunk "$o"
}
lappend chunklist [list result $chunk]
set_last_run_display $chunklist
if {$nonewline} {
return [string trimright $::shellrun::runout \r\n]
} else {
return $::shellrun::runout
}
}
proc runerr {args} {
set_last_run_display [list]
variable runout
variable runerr
set runout ""
set runerr ""
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
if {"-nonewline" in $runopts} {
set nonewline 1
} else {
set nonewline 0
}
set callopts ""
if {"-tcl" in $runopts} {
append callopts " -tclscript 1"
}
if {"-echo" in $runopts} {
set stderr_stackid [shellfilter::stack::add stderr tee_to_var -action float-locked -settings {-varname ::shellrun::runerr}]
set stdout_stackid [shellfilter::stack::add stdout tee_to_var -action float-locked -settings {-varname ::shellrun::runout}]
} else {
set stderr_stackid [shellfilter::stack::add stderr var -action float-locked -settings {-varname ::shellrun::runerr}]
set stdout_stackid [shellfilter::stack::add stdout var -action float-locked -settings {-varname ::shellrun::runout}]
}
set exitinfo [shellfilter::run $cmdargs {*}$callopts -teehandle punk -inbuffering none -outbuffering none -stdinhandler ::repl::repl_handler]
shellfilter::stack::remove stderr $stderr_stackid
shellfilter::stack::remove stdout $stdout_stackid
flush stderr
flush stdout
#we raise an error because an error during calling is different to collecting stderr from a command, and the caller should be able to wrap in a catch
# to determine something other than just a nonzero exit code or output on stderr.
if {[dict exists $exitinfo error]} {
if {"-tcl" in $runopts} {
} else {
#todo - check errorInfo makes sense.. return -code? tailcall?
error [dict get $exitinfo error]
}
}
set chunklist [list]
set n [a]
set c ""
if [dict exists $exitinfo exitcode] {
set code [dict get $exitinfo exitcode]
if {$code == 0} {
set c [a+ green]
} else {
set c [a+ white bold]
}
lappend chunklist [list "info" "$c$exitinfo$n"]
} elseif [dict exists $exitinfo error] {
set c [a+ yellow bold]
lappend chunklist [list "info" "error [dict get $exitinfo error]"]
lappend chunklist [list "info" "errorCode [dict get $exitinfo errorCode]"]
lappend chunklist [list "info" "errorInfo [list [dict get $exitinfo errorInfo]]"]
} else {
set c [a+ Yellow red bold]
lappend chunklist [list "info" "$c$exitinfo$n"]
}
lappend chunklist [list "info" "[a+ white bold]stdout[a]"]
set chunk ""
if {[string length $::shellrun::runout]} {
if {$nonewline} {
set o [string trimright $::shellrun::runout \r\n]
} else {
set o $::shellrun::runout
}
append chunk "[a+ white light]$o[a]\n" ;#this newline is the display output separator - always there whether data has trailing newline or not.
}
lappend chunklist [list stdout $chunk]
set chunk "[a+ red bold]stderr[a]"
lappend chunklist [list "info" $chunk]
set chunk ""
if {[string length $::shellrun::runerr]} {
if {$nonewline} {
set e [string trimright $::shellrun::runerr \r\n]
} else {
set e $::shellrun::runerr
}
append chunk "$e"
}
lappend chunklist [list resulterr $chunk]
set_last_run_display $chunklist
if {$nonewline} {
return [string trimright $::shellrun::runerr \r\n]
}
return $::shellrun::runerr
}
proc runx {args} {
set_last_run_display [list]
variable runout
variable runerr
set runout ""
set runerr ""
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
if {"-nonewline" in $runopts} {
set nonewline 1
} else {
set nonewline 0
}
#shellfilter::stack::remove stdout $::repl::id_outstack
if {"-echo" in $runopts} {
#float to ensure repl transform doesn't interfere with the output data
set stderr_stackid [shellfilter::stack::add stderr tee_to_var -action float -settings {-varname ::shellrun::runerr}]
set stdout_stackid [shellfilter::stack::add stdout tee_to_var -action float -settings {-varname ::shellrun::runout}]
} else {
#set stderr_stackid [shellfilter::stack::add stderr var -action sink-locked -settings {-varname ::shellrun::runerr}]
#set stdout_stackid [shellfilter::stack::add stdout var -action sink-locked -settings {-varname ::shellrun::runout}]
#float above the repl's tee_to_var to deliberately block it.
#a var transform is naturally a junction point because there is no flow-through..
# - but mark it with -junction 1 just to be explicit
set stderr_stackid [shellfilter::stack::add stderr var -action float-locked -junction 1 -settings {-varname ::shellrun::runerr}]
set stdout_stackid [shellfilter::stack::add stdout var -action float-locked -junction 1 -settings {-varname ::shellrun::runout}]
}
set callopts ""
if {"-tcl" in $runopts} {
append callopts " -tclscript 1"
}
#set exitinfo [shellfilter::run $cmdargs -teehandle punk -inbuffering none -outbuffering none -stdinhandler ::repl::repl_handler]
set exitinfo [shellfilter::run $cmdargs {*}$callopts -teehandle punk -inbuffering none -outbuffering none]
shellfilter::stack::remove stdout $stdout_stackid
shellfilter::stack::remove stderr $stderr_stackid
flush stderr
flush stdout
if {[dict exists $exitinfo error]} {
if {"-tcl" in $runopts} {
} else {
#todo - check errorInfo makes sense.. return -code? tailcall?
error [dict get $exitinfo error]
}
}
#set x [shellfilter::stack::add stdout var -action sink-locked -settings {-varname ::repl::runxoutput}]
set chunk ""
if {[string length $::shellrun::runout]} {
if {$nonewline} {
set o [string trimright $::shellrun::runout \r\n]
} else {
set o $::shellrun::runout
}
set chunk $o
}
set chunklist [list]
lappend chunklist [list "info" " "]
lappend chunklist [list "result" stdout] ;#key 'stdout' forms part of the resulting dictionary output
lappend chunklist [list "info" "[a+ white bold]stdout[a]"]
lappend chunklist [list result $chunk] ;#value corresponding to 'stdout' key in resulting dict
lappend chunklist [list "info" " "]
set chunk "[a+ red bold]stderr[a]"
lappend chunklist [list "result" $chunk]
lappend chunklist [list "info" stderr]
set chunk ""
if {[string length $::shellrun::runerr]} {
if {$nonewline} {
set e [string trimright $::shellrun::runerr \r\n]
} else {
set e $::shellrun::runerr
}
set chunk $e
}
#stderr is part of the result
lappend chunklist [list "resulterr" $chunk]
set n [a]
set c ""
if {[dict exists $exitinfo exitcode]} {
set code [dict get $exitinfo exitcode]
if {$code == 0} {
set c [a+ green]
} else {
set c [a+ yellow bold]
}
lappend chunklist [list "info" " "]
lappend chunklist [list "result" exitcode]
lappend chunklist [list "info" "exitcode $code"]
lappend chunklist [list "result" "$c$code$n"]
set exitdict [list exitcode $code]
} elseif {[dict exists $exitinfo result]} {
# presumably from a -tcl call
set val [dict get $exitinfo result]
lappend chunklist [list "info" " "]
lappend chunklist [list "result" result]
lappend chunklist [list "info" result]
lappend chunklist [list "result" $val]
set exitdict [list result $val]
} elseif {[dict exists $exitinfo error]} {
# -tcl call with error
#set exitdict [dict create]
lappend chunklist [list "info" " "]
lappend chunklist [list "result" error]
lappend chunklist [list "info" error]
lappend chunklist [list "result" [dict get $exitinfo error]]
lappend chunklist [list "info" " "]
lappend chunklist [list "result" errorCode]
lappend chunklist [list "info" errorCode]
lappend chunklist [list "result" [dict get $exitinfo errorCode]]
lappend chunklist [list "info" " "]
lappend chunklist [list "result" errorInfo]
lappend chunklist [list "info" errorInfo]
lappend chunklist [list "result" [dict get $exitinfo errorInfo]]
set exitdict $exitinfo
} else {
#review - if no exitcode or result. then what is it?
lappend chunklist [list "info" exitinfo]
set c [a+ yellow bold]
lappend chunklist [list result "$c$exitinfo$n"]
set exitdict [list exitinfo $exitinfo]
}
set_last_run_display $chunklist
#set ::repl::result_print 0
#return [lindex [list [list stdout $::runout stderr $::runerr {*}$exitinfo] [shellfilter::stack::remove stdout $x][puts -nonewline stdout $pretty][set ::repl::output ""]] 0]
if {$nonewline} {
return [list {*}$exitdict stdout [string trimright $::shellrun::runout \r\n] stderr [string trimright $::shellrun::runerr \r\n]]
}
#always return exitinfo $code at beginning of dict (so that punk unknown can interpret the exit code as a unix-style bool if double evaluated)
return [list {*}$exitdict stdout $::shellrun::runout stderr $::shellrun::runerr]
}
#an experiment
#
#run as raw string instead of tcl-list - no variable subst etc
#
#dummy repl_runraw that repl will intercept
proc repl_runraw {args} {
error "runraw: only available in repl as direct call - not from script"
}
#we can only call runraw with a single (presumably braced) string if we want to use it from both repl and tcl scripts (why? todo with unbalanced quotes/braces?)
proc runraw {commandline} {
#runraw fails as intended - because we can't bypass exec/open interference quoting :/
set_last_run_display [list]
variable runout
variable runerr
set runout ""
set runerr ""
#return [shellfilter::run [lrange $args 1 end] -teehandle punk -inbuffering none -outbuffering none -stdinhandler ::repl::repl_handler]
puts stdout ">>runraw got: $commandline"
#run always echoes anyway.. as we aren't diverting stdout/stderr off for capturing
#for consistency with other runxxx commands - we'll just consume it. (review)
set reallyraw 1
if {$reallyraw} {
set wordparts [regexp -inline -all {\S+} $commandline]
set runwords $wordparts
} else {
#shell style args parsing not suitable for windows where we can't assume matched quotes etc.
package require string::token::shell
set parts [string token shell -indices -- $commandline]
puts stdout ">>shellparts: $parts"
set runwords [list]
foreach p $parts {
set ptype [lindex $p 0]
set pval [lindex $p 3]
if {$ptype eq "PLAIN"} {
lappend runwords [lindex $p 3]
} elseif {$ptype eq "D:QUOTED"} {
set v {"}
append v $pval
append v {"}
lappend runwords $v
} elseif {$ptype eq "S:QUOTED"} {
set v {'}
append v $pval
append v {'}
lappend runwords $v
}
}
}
puts stdout ">>runraw runwords: $runwords"
set runwords [lrange $runwords 1 end]
puts stdout ">>runraw runwords: $runwords"
#set args [lrange $args 1 end]
#set runwords [lrange $wordparts 1 end]
set known_runopts [list "-echo" "-e" "-terminal" "-t"]
set aliases [list "-e" "-echo" "-echo" "-echo" "-t" "-terminal" "-terminal" "-terminal"] ;#include map to self
set runopts [list]
set cmdwords [list]
set idx_first_cmdarg [lsearch -not $runwords "-*"]
set runopts [lrange $runwords 0 $idx_first_cmdarg-1]
set cmdwords [lrange $runwords $idx_first_cmdarg end]
foreach o $runopts {
if {$o ni $known_runopts} {
error "runraw: Unknown runoption $o"
}
}
set runopts [lmap o $runopts {dict get $aliases $o}]
set cmd_as_string [join $cmdwords " "]
puts stdout ">>cmd_as_string: $cmd_as_string"
if {"-terminal" in $runopts} {
#fake terminal using 'script' command.
#not ideal: smushes stdout & stderr together amongst other problems
set tcmd [shellfilter::get_scriptrun_from_cmdlist_dquote_if_not $cmdwords]
puts stdout ">>tcmd: $tcmd"
set exitinfo [shellfilter::run $tcmd -teehandle punk -inbuffering line -outbuffering none ]
set exitinfo "exitcode not-implemented"
} else {
set exitinfo [shellfilter::run $cmdwords -teehandle punk -inbuffering line -outbuffering none ]
}
if {[dict exists $exitinfo error]} {
#todo - check errorInfo makes sense.. return -code? tailcall?
error [dict get $exitinfo error]
}
set code [dict get $exitinfo exitcode]
if {$code == 0} {
set c [a+ green]
} else {
set c [a+ white bold]
}
puts stderr $c
return $exitinfo
}
proc sh_run {args} {
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
#e.g sh -c "ls -l *"
#we pass cmdargs to sh -c as a list, not individually
tailcall shellrun::run {*}$runopts sh -c $cmdargs
}
proc sh_runout {args} {
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
tailcall shellrun::runout {*}$runopts sh -c $cmdargs
}
proc sh_runerr {args} {
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
tailcall shellrun::runerr {*}$runopts sh -c $cmdargs
}
proc sh_runx {args} {
set splitargs [get_run_opts $args]
set runopts [dict get $splitargs runopts]
set cmdargs [dict get $splitargs cmdargs]
tailcall shellrun::runx {*}$runopts sh -c $cmdargs
}
}
namespace eval shellrun {
interp alias {} run {} shellrun::run
interp alias {} sh_run {} shellrun::sh_run
interp alias {} runout {} shellrun::runout
interp alias {} sh_runout {} shellrun::sh_runout
interp alias {} runerr {} shellrun::runerr
interp alias {} sh_runerr {} shellrun::sh_runerr
interp alias {} runx {} shellrun::runx
interp alias {} sh_runx {} shellrun::sh_runx
interp alias {} runraw {} shellrun::runraw
#the shortened versions deliberately don't get pretty output from the repl
interp alias {} r {} shellrun::run
interp alias {} ro {} shellrun::runout
interp alias {} re {} shellrun::runerr
interp alias {} rx {} shellrun::runx
}
namespace eval shellrun {
proc test_cffi {} {
package require test_cffi
cffi::Wrapper create ::shellrun::kernel32 [file join $env(windir) system32 Kernel32.dll]
::shellrun::kernel32 stdcall CreateProcessA
#todo - stuff.
return ::shellrun::kernel32
}
}
package provide shellrun [namespace eval shellrun {
variable version
set version 0.1
}]

698
src/modules/punk/mix/templates/layouts/project/src/sample.vfs/modules/shellthread-1.6.tm

@ -0,0 +1,698 @@
#package require logger
package provide shellthread [namespace eval shellthread {
variable version
set version 1.6
}]
package require Thread
namespace eval shellthread {
proc iso8601 {{tsmicros ""}} {
if {$tsmicros eq ""} {
set tsmicros [clock micros]
} else {
set microsnow [clock micros]
if {[string length $tsmicros] != [string length $microsnow]} {
error "iso8601 requires 'clock micros' or empty string to create timestamp"
}
}
set seconds [expr {$tsmicros / 1000000}]
return [clock format $seconds -format "%Y-%m-%d_%H-%M-%S"]
}
}
namespace eval shellthread::worker {
variable settings
variable sysloghost_port
variable sock
variable logfile ""
variable fd
variable client_ids [list]
variable ts_start_micros
variable errorlist [list]
variable inpipe ""
proc bgerror {args} {
variable errorlist
lappend errorlist $args
}
proc send_errors_now {tidcli} {
variable errorlist
thread::send -async $tidcli [list shellthread::manager::report_worker_errors [list worker_tid [thread::id] errors $errorlist]]
}
proc add_client_tid {tidcli} {
variable client_ids
if {$tidcli ni $client_ids} {
lappend client_ids $tidcli
}
}
proc init {tidclient start_m settingsdict} {
variable sysloghost_port
variable logfile
variable settings
interp bgerror {} shellthread::worker::bgerror
package require overtype
variable client_ids
variable ts_start_micros
lappend client_ids $tidclient
set ts_start_micros $start_m
set defaults [list -raw 0 -file "" -syslog "" -direction out]
set settings [dict merge $defaults $settingsdict]
set syslog [dict get $settings -syslog]
if {[string length $syslog]} {
lassign [split $syslog :] s_host s_port
set sysloghost_port [list $s_host $s_port]
} else {
set sysloghost_port ""
}
if {[catch {package require udp} errm]} {
#disable rather than bomb and interfere with any -file being written
set sysloghost_port ""
}
set logfile [dict get $settings -file]
}
proc start_pipe_read {source readchan args} {
#assume 1 inpipe for now
variable inpipe
variable sysloghost_port
variable logfile
set defaults [dict create -buffering \uFFFF ]
set opts [dict merge $defaults $args]
if {[dict exists $opts -readbuffering]} {
set readbuffering [dict get $opts -readbuffering]
} else {
if {[dict get $opts -buffering] eq "\uFFFF"} {
#get buffering setting from the channel as it was set prior to thread::transfer
set readbuffering [chan configure $readchan -buffering]
} else {
set readbuffering [dict get $opts -buffering]
chan configure $readchan -buffering $readbuffering
}
}
if {[dict exists $opts -writebuffering]} {
set writebuffering [dict get $opts -writebuffering]
} else {
if {[dict get $opts -buffering] eq "\uFFFF"} {
set writebuffering line
#set writebuffering [chan configure $writechan -buffering]
} else {
set writebuffering [dict get $opts -buffering]
#can configure $writechan -buffering $writebuffering
}
}
chan configure $readchan -translation lf
if {$readchan ni [chan names]} {
error "shellthread::worker::start_pipe_read - inpipe not configured. Use shellthread::manager::set_pipe_read_from_client to thread::transfer the pipe end"
}
set inpipe $readchan
#::shellthread::worker::log $inpipe 0 - $source - info "START PIPE READ HELLO\n" line
chan configure $readchan -blocking 0
#::shellthread::worker::log $inpipe 0 - $source - info "START PIPE READ HELLO2 readbuffering: $readbuffering syslog $sysloghost_port filename $logfile" line
set waitvar ::shellthread::worker::wait($inpipe,[clock micros])
chan event $readchan readable [list apply {{chan source waitfor readbuffering writebuffering} {
if {$readbuffering eq "line"} {
set chunksize [chan gets $chan chunk]
if {$chunksize >= 0} {
if {![chan eof $chan]} {
::shellthread::worker::log pipe 0 - $source - info $chunk\n $writebuffering
} else {
::shellthread::worker::log pipe 0 - $source - info $chunk $writebuffering
}
}
} else {
set chunk [chan read $chan]
::shellthread::worker::log pipe 0 - $source - info $chunk $writebuffering
}
if {[chan eof $chan]} {
chan event $chan readable {}
set $waitfor "pipe"
chan close $chan
}
}} $readchan $source $waitvar $readbuffering $writebuffering]
#::shellthread::worker::log $inpipe 0 - $source - info "START PIPE READ HELLO3 vwaiting on $waitvar\n" line
vwait $waitvar
}
proc start_pipe_write {source writechan args} {
variable outpipe
set defaults [dict create -buffering \uFFFF ]
set opts [dict merge $defaults $args]
#todo!
set readchan stdin
if {[dict exists $opts -readbuffering]} {
set readbuffering [dict get $opts -readbuffering]
} else {
if {[dict get $opts -buffering] eq "\uFFFF"} {
set readbuffering [chan configure $readchan -buffering]
} else {
set readbuffering [dict get $opts -buffering]
chan configure $readchan -buffering $readbuffering
}
}
if {[dict exists $opts -writebuffering]} {
set writebuffering [dict get $opts -writebuffering]
} else {
if {[dict get $opts -buffering] eq "\uFFFF"} {
#nothing explicitly set - take from transferred channel
set writebuffering [chan configure $writechan -buffering]
} else {
set writebuffering [dict get $opts -buffering]
can configure $writechan -buffering $writebuffering
}
}
if {$writechan ni [chan names]} {
error "shellthread::worker::start_pipe_write - outpipe not configured. Use shellthread::manager::set_pipe_write_to_client to thread::transfer the pipe end"
}
set outpipe $writechan
chan configure $readchan -blocking 0
chan configure $writechan -blocking 0
set waitvar ::shellthread::worker::wait($outpipe,[clock micros])
chan event $readchan readable [list apply {{chan writechan source waitfor readbuffering} {
if {$readbuffering eq "line"} {
set chunksize [chan gets $chan chunk]
if {$chunksize >= 0} {
if {![chan eof $chan]} {
puts $writechan $chunk
} else {
puts -nonewline $writechan $chunk
}
}
} else {
set chunk [chan read $chan]
puts -nonewline $writechan $chunk
}
if {[chan eof $chan]} {
chan event $chan readable {}
set $waitfor "pipe"
chan close $writechan
if {$chan ne "stdin"} {
chan close $chan
}
}
}} $readchan $writechan $source $waitvar $readbuffering]
vwait $waitvar
}
proc _initsock {} {
variable sysloghost_port
variable sock
if {[string length $sysloghost_port]} {
if {[catch {fconfigure $sock} state]} {
set sock [udp_open]
fconfigure $sock -buffering none -translation binary
fconfigure $sock -remote $sysloghost_port
}
}
}
proc _reconnect {} {
variable sock
catch {close $sock}
_initsock
return [fconfigure $sock]
}
proc send_info {client_tid ts_sent source msg} {
set ts_received [clock micros]
set lag_micros [expr {$ts_received - $ts_sent}]
set lag [expr {$lag_micros / 1000000.0}] ;#lag as x.xxxxxx seconds
log $client_tid $ts_sent $lag $source - info $msg line 1
}
proc log {client_tid ts_sent lag source service level msg writebuffering {islog 0}} {
variable sock
variable fd
variable sysloghost_port
variable logfile
variable settings
set logchunk $msg
if {![dict get $settings -raw]} {
set tail_crlf 0
set tail_lf 0
set tail_cr 0
#for cooked - always remove the trailing newline before splitting..
#
#note that if we got our data from reading a non-line-buffered binary channel - then this naive line splitting will not split neatly for mixed line-endings.
#
#Possibly not critical as cooked is for logging and we are still preserving all \r and \n chars - but review and consider implementing a better split
#but add it back exactly as it was afterwards
#we can always split on \n - and any adjacent \r will be preserved in the rejoin
set lastchar [string range $logchunk end end]
if {[string range $logchunk end-1 end] eq "\r\n"} {
set tail_crlf 1
set logchunk [string range $logchunk 0 end-2]
} else {
if {$lastchar eq "\n"} {
set tail_lf 1
set logchunk [string range $logchunk 0 end-1]
} elseif {$lastchar eq "\r"} {
#\r line-endings are obsolete..and unlikely... and ugly as they can hide characters on the console. but we'll pass through anyway.
set tail_cr 1
set logchunk [string range $logchunk 0 end-1]
} else {
#possibly a single line with no linefeed.. or has linefeeds only in the middle
}
}
if {$ts_sent != 0} {
set micros [lindex [split [expr {$ts_sent / 1000000.0}] .] end]
set time_info [::shellthread::iso8601 $ts_sent].$micros
#set time_info "${time_info}+$lag"
set lagfp "+[format %f $lag]"
} else {
#from pipe - no ts_sent/lag info available
set time_info ""
set lagfp ""
}
set idtail [string range $client_tid end-8 end] ;#enough for display purposes id - mostly zeros anyway
set col0 [string repeat " " 9]
set col1 [string repeat " " 27]
set col2 [string repeat " " 11]
set col3 [string repeat " " 20]
#do not columnize the final data column or append to tail - or we could muck up the crlf integrity
lassign [list [overtype::left $col0 $idtail] [overtype::left $col1 $time_info] [overtype::left $col2 $lagfp] [overtype::left $col3 $source]] c0 c1 c2 c3
#split on \n no matter the actual line-ending in use
#shouldn't matter as long as we don't add anything at the end of the line other than the raw data
#ie - don't quote or add spaces
set lines [split $logchunk \n]
set i 1
set outlines [list]
foreach ln $lines {
if {$i == 1} {
lappend outlines "$c0 $c1 $c2 $c3 $ln"
} else {
lappend outlines "$c0 $c1 $col2 $c3 $ln"
}
incr i
}
if {$tail_lf} {
set logchunk "[join $outlines \n]\n"
} elseif {$tail_crlf} {
set logchunk "[join $outlines \r\n]\r\n"
} elseif {$tail_cr} {
set logchunk "[join $outlines \r]\r"
} else {
#no trailing linefeed
set logchunk [join $outlines \n]
}
#set logchunk "[overtype::left $col0 $idtail] [overtype::left $col1 $time_info] [overtype::left $col2 "+$lagfp"] [overtype::left $col3 $source] $msg"
}
if {[string length $sysloghost_port]} {
_initsock
catch {puts -nonewline $sock $logchunk}
}
#todo - sockets etc?
if {[string length $logfile]} {
#todo - setting to maintain open filehandle and reduce io.
# possible settings for buffersize - and maybe logrotation, although this could be left to client
#for now - default to safe option of open/close each write despite the overhead.
set fd [open $logfile a]
chan configure $fd -translation auto -buffering $writebuffering
#whether line buffered or not - by now our logchunk includes newlines
puts -nonewline $fd $logchunk
close $fd
}
}
# - withdraw just this client
proc finish {tidclient} {
variable client_ids
if {($tidclient in $clientids) && ([llength $clientids] == 1)} {
terminate $tidclient
} else {
set posn [lsearch $client_ids $tidclient]
set client_ids [lreplace $clientids $posn $posn]
}
}
#allow any client to terminate
proc terminate {tidclient} {
variable sock
variable client_ids
if {$tidclient in $client_ids} {
catch {close $sock}
set client_ids [list]
return 1
} else {
return 0
}
}
}
namespace eval shellthread::manager {
variable workers [dict create]
variable worker_errors [list]
variable free_threads [list]
#variable log_threads
#new datastructure regarding workers and sourcetags required.
#one worker can service multiple sourcetags - but each sourcetag may be used by multiple threads too.
#generally each thread will use a specific sourcetag - but we may have pools doing similar things which log to same destination.
#
#As a convention we may use a sourcetag for the thread which started the worker that isn't actually used for logging - but as a common target for joins
#If the thread which started the thread calls leave_worker with that 'primary' sourcetag it means others won't be able to use that target - which seems reasonable.
#If another thread want's to maintain joinability beyond the span provided by the starting client,
#it can join with both the primary tag and a tag it will actually use for logging.
#A thread can join the logger with any existingtag - not just the 'primary'
#(which is arbitrary anyway. It will usually be the first in the list - but may be unsubscribed by clients and disappear)
proc join_worker {existingtag sourcetaglist} {
set client_tid [thread::id]
#todo - allow a source to piggyback on existing worker by referencing one of the sourcetags already using the worker
}
#it is up to caller to use a unique sourcetag (e.g by prefixing with own thread::id etc)
# This allows multiple threads to more easily write to the same named sourcetag if necessary
# todo - change sourcetag for a list of tags which will be handled by the same thread. e.g for multiple threads logging to same file
#
# todo - some protection mechanism for case where target is a file to stop creation of multiple worker threads writing to same file.
# Even if we use open fd,close fd wrapped around writes.. it is probably undesirable to have multiple threads with same target
# On the other hand socket targets such as UDP can happily be written to by multiple threads.
# For now the mechanism is that a call to new_worker (rename to open_worker?) will join the same thread if a sourcetag matches..
# but, as sourcetags can get removed(unsubbed via leave_worker) this doesn't guarantee two threads with same -file settings won't fight.
# Also.. the settingsdict is ignored when joining with a tag that exists.. this is problematic.. e.g logrotation where previous file still being written by existing worker
# todo - rename 'sourcetag' concept to 'targettag' ?? the concept is a mixture of both.. it is somewhat analagous to a syslog 'facility'
# probably new_worker should disallow auto-joining and we allow different workers to handle same tags simultaneously to support overlap during logrotation etc.
proc new_worker {sourcetaglist {settingsdict {}}} {
variable workers
set ts_start [clock micros]
set tidclient [thread::id]
set sourcetag [lindex $sourcetaglist 0] ;#todo - use all
if {[dict exists $workers $sourcetag]} {
set winfo [dict get $workers $sourcetag]
if {[thread::exists [dict get $winfo tid]]} {
#add our client-info to existing worker thread
dict lappend winfo list_client_tids $tidclient
dict set workers $sourcetag $winfo ;#writeback
return [dict get $winfo tid]
}
}
#check if there is an existing unsubscribed thread first
variable free_threads
if {[llength $free_threads]} {
#todo - re-use from tail - as most likely to have been doing similar work?? review
set free_threads [lassign $free_threads tidworker]
#todo - keep track of real ts_start of free threads... kill when too old
set winfo [dict create tid $tidworker list_client_tids [list $tidclient] ts_start $ts_start ts_end_list [list]]
puts stderr "shellfilter::new_worker Re-using free worker thread: $tidworker with tag $sourcetag"
dict set workers $sourcetag $winfo
return $tidworker
}
#set ts_start [::shellthread::iso8601]
set tidworker [thread::create -preserved]
set init_script [string map [list %ts_start% $ts_start %mp% [tcl::tm::list] %ap% $::auto_path %tidcli% $tidclient %sd% $settingsdict] {
#set tclbase [file dirname [file dirname [info nameofexecutable]]]
#set tcllib $tclbase/lib
#if {$tcllib ni $::auto_path} {
# lappend ::auto_path $tcllib
#}
set ::settingsinfo [dict create %sd%]
#if the executable running things is something like a tclkit,
# then it's likely we will need to use the caller's auto_path and tcl::tm::list to find things
#The caller can tune the thread's package search by providing a settingsdict
if {![dict exists $::settingsinfo tcl_tm_list]} {
::tcl::tm::add %mp%
} else {
tcl::tm::remove {*}[tcl::tm::list]
::tcl::tm::add {*}[dict get $::settingsinfo tcl_tm_list]
}
if {![dict exists $::settingsinfo auto_path]} {
set ::auto_path [list %ap%]
} else {
set ::auto_path [dict get $::settingsinfo auto_path]
}
package require Thread
package require shellthread
if {![catch {::shellthread::worker::init %tidcli% %ts_start% $::settingsinfo} errmsg]} {
unset ::settingsinfo
set ::shellthread_init "ok"
} else {
unset ::settingsinfo
set ::shellthread_init "err $errmsg"
}
}]
thread::send -async $tidworker $init_script
#thread::send $tidworker $init_script
set winfo [dict create tid $tidworker list_client_tids [list $tidclient] ts_start $ts_start ts_end_list [list]]
dict set workers $sourcetag $winfo
return $tidworker
}
proc set_pipe_read_from_client {tag_pipename worker_tid rchan args} {
variable workers
if {![dict exists $workers $tag_pipename]} {
error "workerthread::manager::set_pipe_read_from_client source/pipename $tag_pipename not found"
}
set match_worker_tid [dict get $workers $tag_pipename tid]
if {$worker_tid ne $match_worker_tid} {
error "workerthread::manager::set_pipe_read_from_client source/pipename $tag_pipename workert_tid mismatch '$worker_tid' vs existing:'$match_worker_tid'"
}
#buffering set during channel creation will be preserved on thread::transfer
thread::transfer $worker_tid $rchan
#start_pipe_read will vwait - so we have to send async
thread::send -async $worker_tid [list ::shellthread::worker::start_pipe_read $tag_pipename $rchan]
#client may start writing immediately - but presumably it will buffer in fifo2
}
proc set_pipe_write_to_client {tag_pipename worker_tid wchan args} {
variable workers
if {![dict exists $workers $tag_pipename]} {
error "workerthread::manager::set_pipe_write_to_client pipename $tag_pipename not found"
}
set match_worker_tid [dict get $workers $tag_pipename tid]
if {$worker_tid ne $match_worker_tid} {
error "workerthread::manager::set_pipe_write_to_client pipename $tag_pipename workert_tid mismatch '$worker_tid' vs existing:'$match_worker_tid'"
}
#buffering set during channel creation will be preserved on thread::transfer
thread::transfer $worker_tid $wchan
thread::send -async $worker_tid [list ::shellthread::worker::start_pipe_write $tag_pipename $wchan]
}
proc write_log {source msg args} {
variable workers
set ts_micros_sent [clock micros]
set defaults [list -async 1 -level info]
set opts [dict merge $defaults $args]
if {[dict exists $workers $source]} {
set tidworker [dict get $workers $source tid]
if {![thread::exists $tidworker]} {
set tidworker [new_worker $source]
}
} else {
#auto create with no requirement to call new_worker.. warn?
set tidworker [new_worker $source]
}
set client_tid [thread::id]
if {[dict get $opts -async]} {
thread::send -async $tidworker [list ::shellthread::worker::send_info $client_tid $ts_micros_sent $source $msg]
} else {
thread::send $tidworker [list ::shellthread::worker::send_info $client_tid $ts_micros_sent $source $msg]
}
}
proc report_worker_errors {errdict} {
variable workers
set reporting_tid [dict get $errdict worker_tid]
dict for {src srcinfo} $workers {
if {[dict get $srcinfo tid] eq $reporting_tid} {
dict set srcinfo errors [dict get $errdict errors]
dict set workers $src $srcinfo ;#writeback updated
break
}
}
}
#aka leave_worker
#Note that the tags may be on separate workertids, or some tags may share workertids
proc unsubscribe {sourcetaglist} {
variable workers
#workers structure example:
#[list sourcetag1 [list tid <tidworker> list_client_tids <clients>] ts_start <ts_start> ts_end_list {}]
variable free_threads
set mytid [thread::id] ;#caller of shellthread::manager::xxx is the client thread
set subscriberless_tags [list]
foreach source $sourcetaglist {
if {[dict exists $workers $source]} {
set list_client_tids [dict get $workers $source list_client_tids]
if {[set posn [lsearch $list_client_tids $mytid]] >= 0} {
set list_client_tids [lreplace $list_client_tids $posn $posn]
dict set workers $source list_client_tids $list_client_tids
}
if {![llength $list_client_tids]} {
lappend subscriberless_tags $source
}
}
}
#we've removed our own tid from all the tags - possibly across multiplew workertids, and possibly leaving some workertids with no subscribers for a particular tag - or no subscribers at all.
set subscriberless_workers [list]
set shuttingdown_workers [list]
foreach deadtag $subscriberless_tags {
set workertid [dict get $workers $deadtag tid]
set worker_tags [get_worker_tagstate $workertid]
set subscriber_count 0
set kill_count 0 ;#number of ts_end_list entries - even one indicates thread is doomed
foreach taginfo $worker_tags {
incr subscriber_count [llength [dict get $taginfo list_client_tids]]
incr kill_count [llength [dict get $taginfo ts_end_list]]
}
if {$subscriber_count == 0} {
lappend subscriberless_workers $workertid
}
if {$kill_count > 0} {
lappend shuttingdown_workers $workertid
}
}
#if worker isn't shutting down - add it to free_threads list
foreach workertid $subscriberless_workers {
if {$workertid ni $shuttingdown_workers} {
if {$workertid ni $free_threads} {
lappend free_threads $workertid
}
}
}
#todo
#unsub this client_tid from the sourcetags in the sourcetaglist. if no more client_tids exist for sourcetag, remove sourcetag,
#if no more sourcetags - add worker to free_threads
}
proc get_worker_tagstate {workertid} {
variable workers
set taginfo_list [list]
dict for {source sourceinfo} $workers {
if {[dict get $sourceinfo tid] eq $workertid} {
lappend taginfo_list $sourceinfo
}
}
return $taginfo_list
}
#instruction to shut-down the thread that has this source.
proc close_worker {source {timeout 2500}} {
variable workers
variable worker_errors
variable free_threads
set ts_now [clock micros]
#puts stderr "close_worker $source"
if {[dict exists $workers $source]} {
set tidworker [dict get $workers $source tid]
if {$tidworker in $freethreads} {
#make sure a thread that is being closed is removed from the free_threads list
set posn [lsearch $freethreads $tidworker]
set freethreads [lreplace $freethreads $posn $posn]
}
set mytid [thread::id]
set client_tids [dict get $workers $source list_client_tids]
if {[set posn [lsearch $client_tids $mytid]] >= 0} {
set client_tids [lreplace $client_tids $posn $posn]
#remove self from list of clients
dict set workers $source list_client_tids $client_tids
}
set ts_end_list [dict get $workers $source ts_end_list] ;#ts_end_list is just a list of timestamps of closing calls for this source - only one is needed to close, but they may all come in a flurry.
if {[llength $ts_end_list]} {
set last_end_ts [lindex $ts_end_list end]
if {[expr {(($tsnow - $last_end_ts) / 1000) >= $timeout}]} {
lappend ts_end_list $ts_now
dict set workers $source ts_end_list $ts_end_list
} else {
#existing close in progress.. assume it will work
return
}
}
if {[thread::exists $tidworker]} {
#puts stderr "shellthread::manager::close_worker: thread $tidworker for source $source still running.. terminating"
set timeoutarr($source) 0
after $timeout [list set timeoutarr($source) 2]
thread::send -async $tidworker [list shellthread::worker::send_errors_now [thread::id]]
thread::send -async $tidworker [list shellthread::worker::terminate [thread::id]] timeoutarr($source)
#thread::send -async $tidworker [string map [list %tidclient% [thread::id]] {
# shellthread::worker::terminate %tidclient%
#}] timeoutarr($source)
vwait timeoutarr($source)
#puts stderr "shellthread::manager::close_worker: thread $tidworker for source $source DONE1"
thread::release $tidworker
#puts stderr "shellthread::manager::close_worker: thread $tidworker for source $source DONE2"
if {[dict exists $workers $source errors]} {
set errlist [dict get $workers $source errors]
if {[llength $errlist]} {
lappend worker_errors [list $source [dict get $workers $source]]
}
}
dict unset workers $source
} else {
#thread may have been closed by call to close_worker with another source with same worker
#clear workers record for this source
#REVIEW - race condition for re-creation of source with new workerid?
#check that record is subscriberless to avoid this
if {[llength [dict get $workers $source list_client_tids]] == 0} {
dict unset workers $source
}
}
}
#puts stdout "close_worker $source - end"
}
#worker errors only available for a source after close_worker called on that source
#It is possible for there to be multiple entries for a source because new_worker can be called multiple times with same sourcetag,
# e.g if a thread
proc get_and_clear_errors {source} {
variable worker_errors
set source_errors [lsearch -all -inline -index 0 $worker_errors $source]
set worker_errors [lsearch -all -inline -index 0 -not $worker_errors $source]
return $source_errors
}
}

49
src/modules/punk/mix/templates/module/template_anyname-0.0.1.tm

@ -1,49 +0,0 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2023
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
foreach base [tcl::tm::list] {
set nsprefix "";#in case sourced directly and not in any of the .tm paths
if {[string match -nocase ${base}* [info script]]} {
set nsprefix [string trimleft [join [lrange [file split [string range [info script] [string length $base]+1 end]] 0 end-1] ::]:: ::]
break
}
}
namespace eval [lassign [split [file rootname [file tail [info script] ]] -] pkgtail verparts]${nsprefix}$pkgtail {
#--------------------------------------
#Do not put any 'package require' statements above this block. (globals nsprefix,pkgtail,verparts still set)
variable pkg "${::nsprefix}${::pkgtail}[unset ::nsprefix; unset ::pkgtail]"
variable version [join $::verparts -][unset ::verparts]
#--------------------------------------
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
namespace eval [namespace current]::lib {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
uplevel #0 [list package provide $pkg $version]
}
return

0
src/modules/punk/mix/templates/module/modulename_buildversion.txt → src/modules/punk/mix/templates/modules/modulename_buildversion.txt

0
src/modules/punk/mix/templates/module/modulename_description.txt → src/modules/punk/mix/templates/modules/modulename_description.txt

0
src/modules/punk/mix/templates/module/template_anyname-0.0.2.tm → src/modules/punk/mix/templates/modules/template_anyname-0.0.2.tm

0
src/modules/punk/mix/templates/module/template_cli-0.0.1.tm → src/modules/punk/mix/templates/modules/template_cli-0.0.1.tm

52
src/modules/punk/mix/templates/modules/template_module-0.0.1.tm

@ -0,0 +1,52 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable pkg %pkg%
variable version
set version 999999.0a1.0
}]
return

0
src/modules/punk/mix/templates/module/template_moduleexactversion-0.0.1.tm → src/modules/punk/mix/templates/modules/template_moduleexactversion-0.0.1.tm

26
src/modules/punk/repo-999999.0a1.0.tm

@ -767,6 +767,30 @@ namespace eval punk::repo {
return $root_dict
}
proc fossil_get_repository_file {{path {}}} {
if {$path eq {}} { set path [pwd] }
set fossilcmd [auto_execok fossil]
if {[llength $fossilcmd]} {
do_in_path $path {
set fossilinfo [::exec {*}$fossilcmd info]
}
set matching_lines [punk::repo::grep {repository:*} $fossilinfo]
if {![llength $matching_lines]} {
return ""
}
set trimmedline [string trim [lindex $matching_lines 0]]
set firstcolon [string first : $trimmedline]
set repofile_path [string trim [string range $trimmedline $firstcolon+1 end]]
if {![file exists $repofile_path]} {
puts stderr "Repository file pointed to by fossil configdb doesn't exist: $repofile_path"
return ""
}
return $repofile_path
} else {
puts stderr "fossil_get_repository_file: fossil command unavailable"
return ""
}
}
proc fossil_get_repository_folder_for_project {projectname args} {
set defaults [list -parentfolder \uFFFF -extrachoice \uFFFF]
@ -1040,7 +1064,7 @@ namespace eval punk::repo {
do_in_path $path {
set info [::exec {*}$fossilcmd remote ls]
}
return [string trim $v]
return [string trim $info]
} else {
return Unknown
}

70
src/modules/punkcheck-0.1.0.tm

@ -1141,6 +1141,7 @@ namespace eval punkcheck {
set opt_antiglob_file_core [dict get $opts -antiglob_file_core]
if {$opt_antiglob_file_core eq "\uFFFF"} {
set opt_antiglob_file_core [default_antiglob_file_core]
dict set opts -antiglob_file_core $opt_antiglob_file_core
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_antiglob_file [dict get $opts -antiglob_file]
@ -1148,6 +1149,7 @@ namespace eval punkcheck {
set opt_antiglob_dir_core [dict get $opts -antiglob_dir_core]
if {$opt_antiglob_dir_core eq "\uFFFF"} {
set opt_antiglob_dir_core [default_antiglob_dir_core]
dict set opts -antiglob_dir_core $opt_antiglob_dir_core
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_antiglob_dir [dict get $opts -antiglob_dir]
@ -1173,6 +1175,7 @@ namespace eval punkcheck {
} else {
set opt_source_checksum 0
}
dict set opts -source_checksum $opt_source_checksum
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_punkcheck_folder [dict get $opts -punkcheck_folder]
@ -1218,13 +1221,15 @@ namespace eval punkcheck {
if {$CALLDEPTH == 0} {
set punkcheck_eventid "<invalid>"
if {$punkcheck_folder ne ""} {
set config [dict create\
-glob $fileglob\
-antiglob_file_core $opt_antiglob_file_core\
-antiglob_file $opt_antiglob_file\
-antiglob_dir_core $opt_antiglob_dir_core\
-antiglob_dir $opt_antiglob_dir\
]
set config $opts
dict unset config -call-depth-internal
dict unset config -max_depth
dict unset config -subdirlist
dict for {k v} $config {
if {$v eq "\uFFFF"} {
dict unset config $k
}
}
lassign [punkcheck::start_installer_event $punkcheck_file $opt_installer $srcdir $tgtdir $config] _eventid punkcheck_eventid _recordset punkcheck_records
}
} else {
@ -1259,11 +1264,23 @@ namespace eval punkcheck {
}
#normalize? review/test
set relative_target_dir [lib::path_relative $tgtdir $current_target_dir]
if {$relative_target_dir eq "."} {
set relative_target_dir ""
}
set relative_source_dir [lib::path_relative $srcdir $current_source_dir]
if {$relative_source_dir eq "."} {
set relative_source_dir ""
}
set target_relative_to_punkcheck_dir [lib::path_relative $punkcheck_folder $current_target_dir]
if {$target_relative_to_punkcheck_dir eq "."} {
set target_relative_to_punkcheck_dir ""
}
foreach unpub $opt_unpublish_paths {
if {[globmatchpath $unpub $current_source_dir]} {
#puts "testing folder - globmatchpath $unpub $relative_source_dir"
if {[globmatchpath $unpub $relative_source_dir]} {
lappend unpublish_paths_matched $current_source_dir
return [list files_copied {} files_skipped {} sources_unchanged {} punkcheck_records $punkcheck_records]
return [list files_copied {} files_skipped {} sources_unchanged {} punkcheck_records $punkcheck_records unpublish_paths_matched $unpublish_paths_matched]
}
}
@ -1326,9 +1343,13 @@ namespace eval punkcheck {
#puts stdout "Current target dir: $current_target_dir"
foreach m $match_list {
set relative_target_path [file join $relative_target_dir $m]
set relative_source_path [file join $relative_source_dir $m]
set punkcheck_target_relpath [file join $target_relative_to_punkcheck_dir $m]
set is_unpublished 0
foreach unpub $opt_unpublish_paths {
if {[globmatchpath $unpub $current_source_dir/$m]} {
#puts "testing file - globmatchpath $unpub vs $relative_source_path"
if {[globmatchpath $unpub $relative_source_path]} {
lappend unpublish_paths_matched $current_source_dir
set is_unpublished 1
break
@ -1342,20 +1363,20 @@ namespace eval punkcheck {
set seconds [expr {$ts_start / 1000000}]
set ts_start_iso [clock format $seconds -format "%Y-%m-%dT%H:%M:%S"]
set relative_target_path [lib::path_relative $punkcheck_folder $current_target_dir/$m]
#puts stdout " rel_target: $relative_target_path"
#puts stdout " rel_target: $punkcheck_target_relpath"
set fetch_filerec_result [punkcheck::recordlist::get_file_record $relative_target_path $punkcheck_records]
set fetch_filerec_result [punkcheck::recordlist::get_file_record $punkcheck_target_relpath $punkcheck_records]
#change to use extract_or_create_fileset_record ?
set existing_filerec_posn [dict get $fetch_filerec_result position]
if {$existing_filerec_posn == -1} {
puts stdout "NO existing record for $relative_target_path"
puts stdout "NO existing record for $punkcheck_target_relpath"
set has_filerec 0
set new_filerec [dict create tag FILEINFO -targets $relative_target_path]
set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath]
set filerec $new_filerec
} else {
set has_filerec 1
#puts stdout " TDL existing FILEINFO record for $relative_target_path"
#puts stdout " TDL existing FILEINFO record for $punkcheck_target_relpath"
#puts stdout " $existing_install_record"
set filerec [dict get $fetch_filerec_result record]
}
@ -1478,7 +1499,8 @@ namespace eval punkcheck {
file mkdir $current_target_dir/$d
}
set sub_result [punkcheck::install $srcdir $tgtdir\
set sub_opts_1 [list\
-call-depth-internal [expr {$CALLDEPTH + 1}]\
-subdirlist [list {*}$subdirlist $d]\
-glob $fileglob\
@ -1493,6 +1515,15 @@ namespace eval punkcheck {
-punkcheck_records $punkcheck_records\
-installer $opt_installer\
]
set sub_opts [list\
-call-depth-internal [expr {$CALLDEPTH + 1}]\
-subdirlist [list {*}$subdirlist $d]\
-punkcheck_folder $punkcheck_folder\
-punkcheck_eventid $punkcheck_eventid\
-punkcheck_records $punkcheck_records\
]
set sub_opts [dict merge $opts $sub_opts]
set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts]
lappend files_copied {*}[dict get $sub_result files_copied]
lappend files_skipped {*}[dict get $sub_result files_skipped]
@ -1504,10 +1535,11 @@ namespace eval punkcheck {
if {[string match *store* $opt_source_checksum]} {
#puts "subdirlist: $subdirlist"
if {$CALLDEPTH == 0} {
if {[llength $files_copied]} {
if {[llength $files_copied] || [llength $files_skipped]} {
puts stdout ">>>>>>>>>>>>>>>>>>>"
set saveresult [punkcheck::save_records_to_file $punkcheck_records $punkcheck_file]
puts stdout "[dict get $saveresult recordcount] records saved as [dict get $saveresult linecount] lines to $punkcheck_file"
puts stdout "copied: [llength $files_copied] skipped: [llength $files_skipped]"
puts stdout ">>>>>>>>>>>>>>>>>>>"
} else {
#todo - write db INSTALLER record if -debug true

23
src/punk86.vfs/lib/app-punk/repl.tcl

@ -75,13 +75,18 @@ package require Thread
#These are strong dependencies
# - the repl requires Threading and punk,shellfilter,shellrun to call and display properly.
# tm list already indexed - need 'package forget' to find modules based on current tcl::tm::list
package forget shellfilter
package require shellfilter
package forget shellrun
package require shellrun
package forget punk
package require punk
set required [list\
shellfilter\
shellrun\
punk\
]
catch {
foreach pkg $required {
package forget $pkg
package require $pkg
}
}
#restore module paths
@ -95,6 +100,10 @@ foreach p $original_tm_list {
}
#------------------------------------------------------------------------------
foreach pkg $required {
package require $pkg
}
package require punk::repl
repl::start stdin

2
src/runtime/mapvfs.config

@ -2,3 +2,5 @@
#if runtime has no entry - it will only match a .vfs folder with a matching filename e.g runtime1.exe runtime1.vfs
tclkit86bi.exe punk86.vfs
tclkit87a5bawt.exe punk86.vfs
#tclkit86bi.exe vfs_windows/punk86win.vfs

Loading…
Cancel
Save