Browse Source

ansi and make.tcl fixes

master
Julian Noble 1 month ago
parent
commit
fac00026fb
  1. 6
      src/bootsupport/modules/punk/args-0.2.tm
  2. 129
      src/bootsupport/modules/punkcheck-0.1.0.tm
  3. 1
      src/bootsupport/modules/textblock-0.1.3.tm
  4. 34
      src/make.tcl
  5. 36
      src/modules/punk/ansi-999999.0a1.0.tm
  6. 28
      src/modules/punk/args-999999.0a1.0.tm
  7. 4
      src/modules/punk/args/tclcore-999999.0a1.0.tm
  8. 3
      src/modules/punk/repl-999999.0a1.0.tm
  9. 121
      src/modules/punkcheck-0.1.0.tm
  10. 47
      src/modules/textblock-999999.0a1.0.tm
  11. 34
      src/project_layouts/custom/_project/punk.basic/src/make.tcl
  12. 6
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.tm
  13. 129
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punkcheck-0.1.0.tm
  14. 1
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm
  15. 34
      src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl
  16. 6
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.tm
  17. 129
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punkcheck-0.1.0.tm
  18. 1
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm
  19. 34
      src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl
  20. 6
      src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm
  21. 125
      src/vfs/_vfscommon.vfs/modules/punkcheck-0.1.0.tm
  22. 1
      src/vfs/_vfscommon.vfs/modules/textblock-0.1.3.tm

6
src/bootsupport/modules/punk/args-0.2.tm

@ -4468,13 +4468,13 @@ tcl::namespace::eval punk::args {
#error "punk::args::parse Even number of -flag val pairs required after arglist" #error "punk::args::parse Even number of -flag val pairs required after arglist"
#} #}
#Default the -errorstyle to enhanced #Default the -errorstyle to standard
# (slowest on unhappy path - but probably clearest for playing with new APIs interactively) # (slow on unhappy path - but probably clearest for playing with new APIs interactively)
# - application devs should distribute a config file with an errorstyle override if desired. # - application devs should distribute a config file with an errorstyle override if desired.
# - devs who prefer a different default for interactive use should create a config for it. (todo) # - devs who prefer a different default for interactive use should create a config for it. (todo)
set defaultopts [dict create\ set defaultopts [dict create\
-form {*}\ -form {*}\
-errorstyle enhanced\ -errorstyle standard\
] ]
#todo - load override_errorstyle from configuration #todo - load override_errorstyle from configuration

129
src/bootsupport/modules/punkcheck-0.1.0.tm

@ -32,9 +32,16 @@ package require punk::mix::util
#see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113 #see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113
# #
namespace eval punkcheck { namespace eval punkcheck {
#namespace export\
# uuid\
# start_installer_event installfile_*
namespace export\ namespace export\
uuid\ uuid\
start_installer_event installfile_* installtrack\
install\
install_tm_files\
install_non_tm_files\
summarize_install_resultdict\
#antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators #antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators
variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"] variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"]
@ -48,7 +55,7 @@ namespace eval punkcheck {
} }
if {!$has_twapi} { if {!$has_twapi} {
if {[catch {package require uuid} errM]} { if {[catch {package require uuid} errM]} {
error "Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows" error "punkcheck: Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows"
} }
return [uuid::uuid generate] return [uuid::uuid generate]
} else { } else {
@ -150,6 +157,8 @@ namespace eval punkcheck {
} }
} }
#instances created by an installtrack object in method start_event
#also in installtrack constructor - to represent existing events from the .punkcheck data
oo::class create installevent { oo::class create installevent {
variable o_id variable o_id
variable o_rel_sourceroot variable o_rel_sourceroot
@ -266,7 +275,7 @@ namespace eval punkcheck {
set o_operation [string toupper $operation] set o_operation [string toupper $operation]
if {$o_operation_start_ts ne ""} { if {$o_operation_start_ts ne ""} {
error "[self] targetset_tart $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish." error "[self] targetset_init $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish."
} }
set o_operation_start_ts [clock microseconds] set o_operation_start_ts [clock microseconds]
set seconds [expr {$o_operation_start_ts / 1000000}] set seconds [expr {$o_operation_start_ts / 1000000}]
@ -1241,7 +1250,17 @@ namespace eval punkcheck {
lappend PUNKARGS [list { lappend PUNKARGS [list {
@id -id ::punkcheck::install @id -id ::punkcheck::install
@cmd -name ::punkcheck::install -help\ @cmd -name ::punkcheck::install -help\
"Unidirectional file transfer to possibly non-empty target folder." "Unidirectional file transfer to possibly non-empty target folder.
This is the simpler form of the API, performing a transfer from one
directory tree to another, copying each file when changes in the source
file are detected.
Changes are detected by content checksum. The first install will record
source checksums in a .punkcheck file (ideally located at the root of the
target folder). Subsequent installs will compare stored checksums with
the current checksums of the source files.
For more advanced install operations, the object command installtrack
can be used to define install operations. e.g when the transfer is not
one-to-one and a target file depends on multiple source files."
@leaders -min 2 -max 2 @leaders -min 2 -max 2
srcdir -type directory srcdir -type directory
tgtdir -type directory tgtdir -type directory
@ -1251,7 +1270,7 @@ namespace eval punkcheck {
"Deepest subdirectory - use -1 for no limit." "Deepest subdirectory - use -1 for no limit."
-createdir -type boolean -default 0 -help\ -createdir -type boolean -default 0 -help\
"Whether to create the folder at tgtdir. "Whether to create the folder at tgtdir.
Any required subdirectories are created regardless of this setting." Any required subdirectories are created regardless of this setting."
-createempty -type boolean -default 0 -help\ -createempty -type boolean -default 0 -help\
"Whether to create folders at target that had no matches for our glob" "Whether to create folders at target that had no matches for our glob"
-glob -type string -default "*" -help\ -glob -type string -default "*" -help\
@ -1282,8 +1301,8 @@ namespace eval punkcheck {
} }
-punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\ -punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\
"The location of the .punkcheck file to track installations and checksums. "The location of the .punkcheck file to track installations and checksums.
The default value 'target' is generally recommended. The default value 'target' is generally recommended.
Can also be an absolute path to a folder." Can also be an absolute path to a folder."
-punkcheck_records -default "" -help\ -punkcheck_records -default "" -help\
"Empty string or a parsed TDL records structure. "Empty string or a parsed TDL records structure.
e.g e.g
@ -1294,7 +1313,14 @@ namespace eval punkcheck {
}" }"
-installer -default "punkcheck::install" -help\ -installer -default "punkcheck::install" -help\
"A user nominated string that is stored in the .punkcheck file "A user nominated string that is stored in the .punkcheck file
This might be the name of a script or installation process." This might be the name of a script or installation process."
-progresschannel -default none -type string -help\
"Name of channel e.g stderr, stdout to which progress messages are written.
This includes the tree-like output consisting of dots (or green U) for each
file processed. As the number of files in a tree is not known beforehand,
it isn't useful for a percentage-based progress meter, but it could potentially
be used to drive a spinner if the textual data is not desired.
Setting to none or an invalid channel will deactivate the output."
}] }]
## unidirectional file transfer to possibly non empty folder ## unidirectional file transfer to possibly non empty folder
#default of -overwrite no-targets will only copy files that are missing at the target #default of -overwrite no-targets will only copy files that are missing at the target
@ -1343,6 +1369,7 @@ namespace eval punkcheck {
-punkcheck_eventid "\uFFFF"\ -punkcheck_eventid "\uFFFF"\
-punkcheck_records ""\ -punkcheck_records ""\
-installer punkcheck::install\ -installer punkcheck::install\
-progresschannel none\
] ]
if {([llength $args] %2) != 0} { if {([llength $args] %2) != 0} {
@ -1367,6 +1394,10 @@ namespace eval punkcheck {
set fileglob [dict get $opts -glob] set fileglob [dict get $opts -glob]
set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting
set opt_createempty [dict get $opts -createempty] set opt_createempty [dict get $opts -createempty]
set opt_progresschannel [dict get $opts -progresschannel]
if {$opt_progresschannel in {"" none} || [catch {chan configure $opt_progresschannel}]} {
set opt_progresschannel ""
}
if {$CALLDEPTH == 0} { if {$CALLDEPTH == 0} {
#expensive to normalize but we need to do it at least once #expensive to normalize but we need to do it at least once
@ -1485,6 +1516,7 @@ namespace eval punkcheck {
dict unset config -call-depth-internal dict unset config -call-depth-internal
dict unset config -max_depth dict unset config -max_depth
dict unset config -subdirlist dict unset config -subdirlist
dict unset config -progresschannel
tcl::dict::for {k v} $config { tcl::dict::for {k v} $config {
if {$v eq "\uFFFF"} { if {$v eq "\uFFFF"} {
dict unset config $k dict unset config $k
@ -1602,7 +1634,7 @@ namespace eval punkcheck {
#puts stdout "Current target dir: $current_target_dir" #puts stdout "Current target dir: $current_target_dir"
set last_processed_dir "" set last_depth ""
foreach m $match_list { foreach m $match_list {
set new_tgt_cksum_info [list] set new_tgt_cksum_info [list]
set relative_target_path [file join $relative_target_dir $m] set relative_target_path [file join $relative_target_dir $m]
@ -1620,23 +1652,6 @@ namespace eval punkcheck {
if {$is_antipath} { if {$is_antipath} {
continue continue
} }
#puts stdout " checking file : $current_source_dir/$m"
#give some output - but not too deep
#set thismatchdir [file dirname [file dirname [file join $srcdir $relative_source_dir]]]
#if {$last_processed_dir ne $thismatchdir} {
# puts stdout "\n checking files in $thismatchdir"
# set last_processed_dir $thismatchdir
#} else {
# puts -nonewline stdout .
#}
if {$last_processed_dir ne $CALLDEPTH} {
puts -nonewline stdout \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
puts -nonewline stdout \n[string repeat " " $CALLDEPTH].
flush stdout
set last_processed_dir $CALLDEPTH
} else {
puts -nonewline stdout .
}
set ts_start [clock microseconds] set ts_start [clock microseconds]
set seconds [expr {$ts_start / 1000000}] set seconds [expr {$ts_start / 1000000}]
@ -1649,7 +1664,9 @@ namespace eval punkcheck {
#change to use extract_or_create_fileset_record ? #change to use extract_or_create_fileset_record ?
set existing_filerec_posn [dict get $fetch_filerec_result position] set existing_filerec_posn [dict get $fetch_filerec_result position]
if {$existing_filerec_posn == -1} { if {$existing_filerec_posn == -1} {
puts stdout "NO existing record for $punkcheck_target_relpath" if {$opt_progresschannel ne ""} {
puts stdout "\nNO existing record for $punkcheck_target_relpath"
}
set has_filerec 0 set has_filerec 0
set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath] set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath]
set filerec $new_filerec set filerec $new_filerec
@ -1667,14 +1684,24 @@ namespace eval punkcheck {
unset new_install_record unset new_install_record
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
if {$CALLDEPTH <=1} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
}
flush $opt_progresschannel
##set last_depth $CALLDEPTH ;# done down below
}
}
set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m] set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m]
#puts stdout " rel_source: $relative_source_path" #puts stdout " rel_source: $relative_source_path"
if {[file pathtype $relative_source_path] ne "relative"} { #if {[file pathtype $relative_source_path] ne "relative"} {
#REVIEW
#different volume or root #different volume or root
} #}
#Note this isn't a recordlist function - so it doesn't purely operate on the records #Note this isn't a recordlist function - so it doesn't purely operate on the records
#this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config. #this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config.
#It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't) #It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't)
@ -1683,18 +1710,19 @@ namespace eval punkcheck {
set ts2 [clock milliseconds] set ts2 [clock milliseconds]
set diff [expr {$ts2 - $ts1}] set diff [expr {$ts2 - $ts1}]
if {$diff > 100} { if {$diff > 100} {
#todo -errorchannel
set errprefix ">>> punkcheck:" set errprefix ">>> punkcheck:"
puts stderr "$errprefix performance warning: fetch_metadata for $m took $diff ms." puts stderr "\n$errprefix performance warning: fetch_metadata for $m took $diff ms."
set lb [lindex [dict get $filerec body] end] set lb [lindex [dict get $filerec body] end]
#puts stderr "$errprefix filerec last body record:$lb" #puts stderr "$errprefix filerec last body record:$lb"
set records [dict get $lb body] set records [dict get $lb body]
set lr [lindex $records end] set lr [lindex $records end]
set alg [dict get $lr -cksum_all_opts -cksum_algorithm] set alg [dict get $lr -cksum_all_opts -cksum_algorithm]
if {$alg eq "sha1"} { if {$alg eq "sha1"} {
puts "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])" puts stderr "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])"
puts "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]" puts stderr "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]"
} else { } else {
puts "$errprefix cksum_algorithm: $alg" puts stderr "$errprefix cksum_algorithm: $alg"
} }
} }
@ -1710,6 +1738,7 @@ namespace eval punkcheck {
} }
set is_skip 0 set is_skip 0
set is_new 0
if {$overwrite_what eq "all-targets"} { if {$overwrite_what eq "all-targets"} {
file mkdir $current_target_dir file mkdir $current_target_dir
#-------------------------------------------- #--------------------------------------------
@ -1720,12 +1749,13 @@ namespace eval punkcheck {
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
} else { } else {
if {![file exists $current_target_dir/$m]} { if {![file exists $current_target_dir/$m]} {
puts stderr "punkcheck: first copy to $current_target_dir/$m " #puts stderr "punkcheck: first copy to $current_target_dir/$m "
file mkdir $current_target_dir file mkdir $current_target_dir
file copy $current_source_dir/$m $current_target_dir file copy $current_source_dir/$m $current_target_dir
set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
incr filecount_new incr filecount_new
set is_new 1
} else { } else {
switch -- $overwrite_what { switch -- $overwrite_what {
installedsourcechanged-targets { installedsourcechanged-targets {
@ -1747,14 +1777,16 @@ namespace eval punkcheck {
} }
} }
synced-targets { synced-targets {
#disallow overwriting of target that has been modified by some other mechanism
#review
if {[llength $changed]} { if {[llength $changed]} {
#only overwrite if the target checksum equals the last installed checksum (ie target is in sync with source and so hasn't been customized) #only overwrite if the target file checksum equals the last installed checksum (ie target is in sync with previous source-set and so hasn't been customized)
set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
set is_target_unmodified_since_install 0 set is_target_unmodified_since_install 0
set target_cksum_compare "unknown" set target_cksum_compare "unknown"
set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list
if {[dict exists $latest_install_record -targets_cksums]} { if {[dict exists $latest_install_record -targets_cksums]} {
set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder) set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder)
if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} { if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} {
set is_target_unmodified_since_install 1 set is_target_unmodified_since_install 1
set target_cksum_compare "match" set target_cksum_compare "match"
@ -1797,6 +1829,7 @@ namespace eval punkcheck {
set ts_now [clock microseconds] set ts_now [clock microseconds]
set elapsed_us [expr {$ts_now - $ts_start}] set elapsed_us [expr {$ts_now - $ts_start}]
@ -1827,6 +1860,29 @@ namespace eval punkcheck {
lset punkcheck_records $existing_filerec_posn $filerec lset punkcheck_records $existing_filerec_posn $filerec
} }
#------------------------------------------------------------
if {$is_skip} {
set mark .
} else {
if {$is_new} {
set mark \x1b\[32\;1mN\x1b\[m
} else {
#updated
set mark \x1b\[32\;1mU\x1b\[m
}
}
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH]$mark
flush $opt_progresschannel
set last_depth $CALLDEPTH
} else {
puts -nonewline $opt_progresschannel $mark
}
}
#------------------------------------------------------------
} }
if {$max_depth != -1 && $CALLDEPTH >= $max_depth} { if {$max_depth != -1 && $CALLDEPTH >= $max_depth} {
@ -1905,6 +1961,7 @@ namespace eval punkcheck {
-punkcheck_folder $punkcheck_folder\ -punkcheck_folder $punkcheck_folder\
-punkcheck_eventid $punkcheck_eventid\ -punkcheck_eventid $punkcheck_eventid\
-punkcheck_records $punkcheck_records\ -punkcheck_records $punkcheck_records\
-progresschannel $opt_progresschannel\
] ]
set sub_opts [dict merge $opts $sub_opts] set sub_opts [dict merge $opts $sub_opts]
set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts] set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts]

1
src/bootsupport/modules/textblock-0.1.3.tm

@ -60,6 +60,7 @@ package require punk::ansi
package require punk::lib package require punk::lib
catch {package require patternpunk} catch {package require patternpunk}
package require overtype package require overtype
package require struct::set
#safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list. #safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list.
#2025 - required term::ansi features for altg now built in to textblock #2025 - required term::ansi features for altg now built in to textblock

34
src/make.tcl

@ -1653,7 +1653,7 @@ if {$::punkboot::command eq "bootsupport"} {
file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists
$boot_event targetset_started $boot_event targetset_started
# -- --- --- --- --- --- # -- --- --- --- --- ---
puts "BOOTSUPPORT module$which update: $srcfile -> $tgtfile" puts "\nBOOTSUPPORT module$which update: $srcfile -> $tgtfile"
if {[catch { if {[catch {
file copy -force $srcfile $tgtfile file copy -force $srcfile $tgtfile
} errM]} { } errM]} {
@ -1719,7 +1719,8 @@ if {$::punkboot::command eq "bootsupport"} {
set resultdict [punkcheck::install $sourcemodules $targetroot\ set resultdict [punkcheck::install $sourcemodules $targetroot\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\ -antiglob_paths $antipaths\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
@ -1737,7 +1738,8 @@ if {$::punkboot::command eq "bootsupport"} {
-max_depth 1\ -max_depth 1\
-createempty 0\ -createempty 0\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
flush stdout flush stdout
@ -1784,7 +1786,12 @@ if {$::punkboot::command in {project modules}} {
#install .tm *and other files* #install .tm *and other files*
puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)" puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder -installer make.tcl -overwrite installedsourcechanged-targets -antiglob_paths {README.md include_modules.config}] set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder\
-installer make.tcl\
-overwrite installedsourcechanged-targets\
-antiglob_paths {README.md include_modules.config}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendormodulefolders]} { if {![llength $vendormodulefolders]} {
@ -1810,7 +1817,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
@ -1906,7 +1917,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $projectlibfolders]} { if {![llength $projectlibfolders]} {
@ -1941,7 +1956,12 @@ if {$::punkboot::command in {project modules}} {
set overwrite "installedsourcechanged-targets" set overwrite "installedsourcechanged-targets"
#set overwrite "ALL-TARGETS" #set overwrite "ALL-TARGETS"
puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)" puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)"
set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base -installer make.tcl -overwrite $overwrite -antiglob_paths {README.md}] set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base\
-installer make.tcl\
-overwrite $overwrite\
-antiglob_paths {README.md}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }

36
src/modules/punk/ansi-999999.0a1.0.tm

@ -3789,6 +3789,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set emit "" set emit ""
#set parts [punk::ansi::ta::split_codes $text] #set parts [punk::ansi::ta::split_codes $text]
set parts [punk::ansi::ta::split_codes_single $text] set parts [punk::ansi::ta::split_codes_single $text]
set prevcode ""
foreach {pt code} $parts { foreach {pt code} $parts {
switch -- [llength $codestack] { switch -- [llength $codestack] {
0 { 0 {
@ -3809,6 +3810,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} }
#parts ends on a pt - last code always empty string #parts ends on a pt - last code always empty string
if {$code ne ""} { if {$code ne ""} {
set prevcode $code
set c1c2 [tcl::string::range $code 0 1] set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\ set leadernorm [tcl::string::range [tcl::string::map [list\
\x1b\[ 7CSI\ \x1b\[ 7CSI\
@ -3827,7 +3829,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set codestack [lremove $codestack {*}$dup_posns] set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code lappend codestack $code
} else { } else {
#jjtest
append emit $code
} }
} }
7GFX { 7GFX {
@ -3839,12 +3842,23 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set o_gx_state off set o_gx_state off
} }
} }
#jjtest
append emit $code
} }
default { default {
#other ansi codes #other ansi codes
#jjtest
append emit $code
} }
} }
append emit $code #jjtest
#append emit $code
} else {
#jjtest
#code is only empty when processing final pt
if {$pt eq ""} {
append emit $prevcode
}
} }
} }
return [append emit $R] return [append emit $R]
@ -3882,6 +3896,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set emit "" set emit ""
#set parts [punk::ansi::ta::split_codes $text] #set parts [punk::ansi::ta::split_codes $text]
set parts [punk::ansi::ta::split_codes_single $text] set parts [punk::ansi::ta::split_codes_single $text]
set prevcode ""
foreach {pt code} $parts { foreach {pt code} $parts {
switch -- [llength $codestack] { switch -- [llength $codestack] {
0 { 0 {
@ -3902,6 +3917,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
} }
#parts ends on a pt - last code always empty string #parts ends on a pt - last code always empty string
if {$code ne ""} { if {$code ne ""} {
set prevcode $code
set c1c2 [tcl::string::range $code 0 1] set c1c2 [tcl::string::range $code 0 1]
set leadernorm [tcl::string::range [tcl::string::map [list\ set leadernorm [tcl::string::range [tcl::string::map [list\
\x1b\[ 7CSI\ \x1b\[ 7CSI\
@ -3920,7 +3936,8 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set codestack [lremove $codestack {*}$dup_posns] set codestack [lremove $codestack {*}$dup_posns]
lappend codestack $code lappend codestack $code
} else { } else {
#jjtest
apend emit $code
} }
} }
7GFX { 7GFX {
@ -3932,12 +3949,23 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu
set o_gx_state off set o_gx_state off
} }
} }
#jjtest
append emit $code
} }
default { default {
#other ansi codes #other ansi codes
#jjtest
append emit $code
} }
} }
append emit $code #jjtest
#append emit $code
} else {
#jjtest
#code is only empty when processing final pt
if {$pt eq ""} {
append emit $prevcode
}
} }
} }
return [append emit $R] return [append emit $R]

28
src/modules/punk/args-999999.0a1.0.tm

@ -3169,6 +3169,7 @@ tcl::namespace::eval punk::args {
set arg_error_CLR(goodarg) [a+ green strike] set arg_error_CLR(goodarg) [a+ green strike]
set arg_error_CLR(goodchoice) [a+ reverse] set arg_error_CLR(goodchoice) [a+ reverse]
set arg_error_CLR(linebase_header) [a+ white] set arg_error_CLR(linebase_header) [a+ white]
set arg_error_CLR(linebase) [a+ white]
set arg_error_CLR(cmdname) [a+ brightwhite] set arg_error_CLR(cmdname) [a+ brightwhite]
set arg_error_CLR(groupname) [a+ bold] set arg_error_CLR(groupname) [a+ bold]
set arg_error_CLR(ansiborder) [a+ bold] set arg_error_CLR(ansiborder) [a+ bold]
@ -3380,7 +3381,7 @@ tcl::namespace::eval punk::args {
#set RST [a] #set RST [a]
set RST "\x1b\[m" set RST "\x1b\[0m"
set t "" ;#possible oo table object - may be tested for objectiness at the end so needs to exist. set t "" ;#possible oo table object - may be tested for objectiness at the end so needs to exist.
#REVIEW - risk of accidental indefinite recursion if functions used here also use punk::args::get_dict and there is an argument error #REVIEW - risk of accidental indefinite recursion if functions used here also use punk::args::get_dict and there is an argument error
@ -3442,7 +3443,8 @@ tcl::namespace::eval punk::args {
if {$cmdhelp ne ""} { if {$cmdhelp ne ""} {
lappend blank_header_col "" lappend blank_header_col ""
#set cmdhelp_display [a+ brightwhite]$cmdhelp[a] #set cmdhelp_display [a+ brightwhite]$cmdhelp[a]
set cmdhelp_display [textblock::ansibase_lines $cmdhelp $CLR(linebase_header)] #set cmdhelp_display [textblock::ansibase_lines $cmdhelp $CLR(linebase_header)]
set cmdhelp_display [punk::ansi::ansiwrap_raw $CLR(linebase_header) "" "" $cmdhelp]
} else { } else {
set cmdhelp_display "" set cmdhelp_display ""
} }
@ -3820,7 +3822,7 @@ tcl::namespace::eval punk::args {
set choicelabeldict [Dict_getdef $arginfo -choicelabels {}] set choicelabeldict [Dict_getdef $arginfo -choicelabels {}]
set choiceinfodict [Dict_getdef $arginfo -choiceinfo {}] set choiceinfodict [Dict_getdef $arginfo -choiceinfo {}]
set formattedchoices [dict create] ;#use dict rather than array to preserve order set formattedchoices [dict create] ;#use dict rather than array to preserve order
append help " Choices$prefixmsg$casemsg" append help "Choices$prefixmsg$casemsg"
if {$choicemultiple_max > 1 || $choicemultiple_max == -1} { if {$choicemultiple_max > 1 || $choicemultiple_max == -1} {
if {$choicemultiple_max == -1} { if {$choicemultiple_max == -1} {
append help \n " The value can be a list of $choicemultiple_min or more of these choices" append help \n " The value can be a list of $choicemultiple_min or more of these choices"
@ -4040,8 +4042,16 @@ tcl::namespace::eval punk::args {
$obj configure_column $i -blockalign left $obj configure_column $i -blockalign left
incr i incr i
} }
append help \n[textblock::join -- " " [$obj print]] append help \n[textblock::join -- " " [$obj print]]
#-------------
#todo - tests
#see special case double reset at end of content in textblock class table get_column_by_index
#bug fixed - needed to ensure last two resets were actually concurrent and at end.
#append help "\nbase[a+ green]ab\nc[a]base" ;#ok
#vs
#append help "\nbase[a+ green]a[a]b\nc[a]base" ;#not ok
#-------------
#set ansititle [dict get [$obj configure -title] value] #set ansititle [dict get [$obj configure -title] value]
$obj destroy $obj destroy
} }
@ -4239,9 +4249,13 @@ tcl::namespace::eval punk::args {
if {[info object isa object $t]} { if {[info object isa object $t]} {
set result $t set result $t
} }
} else {
#put original error at bottom of table too
append result \n $msg
} }
} else { } else {
set result $errmsg set result $errmsg
append result \n $msg
} }
if {$as_error} { if {$as_error} {
#add PUNK to the tail end of the more usual -errorcode {TCL WRONGARGS} so we maintain reasonable compat with things looking for TCL WRONGARGS - but also differentiate it. #add PUNK to the tail end of the more usual -errorcode {TCL WRONGARGS} so we maintain reasonable compat with things looking for TCL WRONGARGS - but also differentiate it.
@ -4468,13 +4482,13 @@ tcl::namespace::eval punk::args {
#error "punk::args::parse Even number of -flag val pairs required after arglist" #error "punk::args::parse Even number of -flag val pairs required after arglist"
#} #}
#Default the -errorstyle to enhanced #Default the -errorstyle to standard
# (slowest on unhappy path - but probably clearest for playing with new APIs interactively) # (slow on unhappy path - but probably clearest for playing with new APIs interactively)
# - application devs should distribute a config file with an errorstyle override if desired. # - application devs should distribute a config file with an errorstyle override if desired.
# - devs who prefer a different default for interactive use should create a config for it. (todo) # - devs who prefer a different default for interactive use should create a config for it. (todo)
set defaultopts [dict create\ set defaultopts [dict create\
-form {*}\ -form {*}\
-errorstyle enhanced\ -errorstyle standard\
] ]
#todo - load override_errorstyle from configuration #todo - load override_errorstyle from configuration

4
src/modules/punk/args/tclcore-999999.0a1.0.tm

@ -3425,8 +3425,8 @@ tcl::namespace::eval punk::args::tclcore {
@opts -type string -parsekey "" -group "NESTED LIST OPTIONS" -grouphelp\ @opts -type string -parsekey "" -group "NESTED LIST OPTIONS" -grouphelp\
"These options are used to search lists of lists. They may be used with any other options." "These options are used to search lists of lists. They may be used with any other options."
-stride -type integer -default 1 -typesynopsis strideLength -help\ -stride -type integer -default 1 -typesynopsis strideLength -help\
"If this option is specified, the list is treated as consisting of groups of strideLength elements and the "If this option is specified, the list is treated as consisting of groups of ${$I}strideLength${$NI} elements and the
groups are searched by either their first element or, if the -index option is used, by the element within groups are searched by either their first element or, if the ${$B}-index${$N} option is used, by the element within
each group given by the first index passed to -index (which is then ignored by -index). The resulting each group given by the first index passed to -index (which is then ignored by -index). The resulting
index always points to the first element in a group. index always points to the first element in a group.
The list length must be an integer multiple of strideLength, which in turn must be at least 1. A The list length must be an integer multiple of strideLength, which in turn must be at least 1. A

3
src/modules/punk/repl-999999.0a1.0.tm

@ -2567,7 +2567,8 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
set c [a yellow bold] set c [a yellow bold]
set n [a] set n [a]
rputs stderr $c$result$n #rputs stderr $c$result$n
rputs [punk::ansi::ansiwrap_raw $c "\x1b\[m" "" $result]
#tcl err hint prompt - lowercase #tcl err hint prompt - lowercase
doprompt "p% " doprompt "p% "
} }

121
src/modules/punkcheck-0.1.0.tm

@ -32,9 +32,16 @@ package require punk::mix::util
#see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113 #see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113
# #
namespace eval punkcheck { namespace eval punkcheck {
#namespace export\
# uuid\
# start_installer_event installfile_*
namespace export\ namespace export\
uuid\ uuid\
start_installer_event installfile_* installtrack\
install\
install_tm_files\
install_non_tm_files\
summarize_install_resultdict\
#antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators #antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators
variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"] variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"]
@ -48,7 +55,7 @@ namespace eval punkcheck {
} }
if {!$has_twapi} { if {!$has_twapi} {
if {[catch {package require uuid} errM]} { if {[catch {package require uuid} errM]} {
error "Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows" error "punkcheck: Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows"
} }
return [uuid::uuid generate] return [uuid::uuid generate]
} else { } else {
@ -150,6 +157,8 @@ namespace eval punkcheck {
} }
} }
#instances created by an installtrack object in method start_event
#also in installtrack constructor - to represent existing events from the .punkcheck data
oo::class create installevent { oo::class create installevent {
variable o_id variable o_id
variable o_rel_sourceroot variable o_rel_sourceroot
@ -266,7 +275,7 @@ namespace eval punkcheck {
set o_operation [string toupper $operation] set o_operation [string toupper $operation]
if {$o_operation_start_ts ne ""} { if {$o_operation_start_ts ne ""} {
error "[self] targetset_tart $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish." error "[self] targetset_init $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish."
} }
set o_operation_start_ts [clock microseconds] set o_operation_start_ts [clock microseconds]
set seconds [expr {$o_operation_start_ts / 1000000}] set seconds [expr {$o_operation_start_ts / 1000000}]
@ -1241,7 +1250,17 @@ namespace eval punkcheck {
lappend PUNKARGS [list { lappend PUNKARGS [list {
@id -id ::punkcheck::install @id -id ::punkcheck::install
@cmd -name ::punkcheck::install -help\ @cmd -name ::punkcheck::install -help\
"Unidirectional file transfer to possibly non-empty target folder." "Unidirectional file transfer to possibly non-empty target folder.
This is the simpler form of the API, performing a transfer from one
directory tree to another, copying each file when changes in the source
file are detected.
Changes are detected by content checksum. The first install will record
source checksums in a .punkcheck file (ideally located at the root of the
target folder). Subsequent installs will compare stored checksums with
the current checksums of the source files.
For more advanced install operations, the object command installtrack
can be used to define install operations. e.g when the transfer is not
one-to-one and a target file depends on multiple source files."
@leaders -min 2 -max 2 @leaders -min 2 -max 2
srcdir -type directory srcdir -type directory
tgtdir -type directory tgtdir -type directory
@ -1251,7 +1270,7 @@ namespace eval punkcheck {
"Deepest subdirectory - use -1 for no limit." "Deepest subdirectory - use -1 for no limit."
-createdir -type boolean -default 0 -help\ -createdir -type boolean -default 0 -help\
"Whether to create the folder at tgtdir. "Whether to create the folder at tgtdir.
Any required subdirectories are created regardless of this setting." Any required subdirectories are created regardless of this setting."
-createempty -type boolean -default 0 -help\ -createempty -type boolean -default 0 -help\
"Whether to create folders at target that had no matches for our glob" "Whether to create folders at target that had no matches for our glob"
-glob -type string -default "*" -help\ -glob -type string -default "*" -help\
@ -1282,8 +1301,8 @@ namespace eval punkcheck {
} }
-punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\ -punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\
"The location of the .punkcheck file to track installations and checksums. "The location of the .punkcheck file to track installations and checksums.
The default value 'target' is generally recommended. The default value 'target' is generally recommended.
Can also be an absolute path to a folder." Can also be an absolute path to a folder."
-punkcheck_records -default "" -help\ -punkcheck_records -default "" -help\
"Empty string or a parsed TDL records structure. "Empty string or a parsed TDL records structure.
e.g e.g
@ -1294,7 +1313,14 @@ namespace eval punkcheck {
}" }"
-installer -default "punkcheck::install" -help\ -installer -default "punkcheck::install" -help\
"A user nominated string that is stored in the .punkcheck file "A user nominated string that is stored in the .punkcheck file
This might be the name of a script or installation process." This might be the name of a script or installation process."
-progresschannel -default none -type string -help\
"Name of channel e.g stderr, stdout to which progress messages are written.
This includes the tree-like output consisting of dots (or green U) for each
file processed. As the number of files in a tree is not known beforehand,
it isn't useful for a percentage-based progress meter, but it could potentially
be used to drive a spinner if the textual data is not desired.
Setting to none or an invalid channel will deactivate the output."
}] }]
## unidirectional file transfer to possibly non empty folder ## unidirectional file transfer to possibly non empty folder
#default of -overwrite no-targets will only copy files that are missing at the target #default of -overwrite no-targets will only copy files that are missing at the target
@ -1343,6 +1369,7 @@ namespace eval punkcheck {
-punkcheck_eventid "\uFFFF"\ -punkcheck_eventid "\uFFFF"\
-punkcheck_records ""\ -punkcheck_records ""\
-installer punkcheck::install\ -installer punkcheck::install\
-progresschannel none\
] ]
if {([llength $args] %2) != 0} { if {([llength $args] %2) != 0} {
@ -1367,6 +1394,10 @@ namespace eval punkcheck {
set fileglob [dict get $opts -glob] set fileglob [dict get $opts -glob]
set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting
set opt_createempty [dict get $opts -createempty] set opt_createempty [dict get $opts -createempty]
set opt_progresschannel [dict get $opts -progresschannel]
if {$opt_progresschannel in {"" none} || [catch {chan configure $opt_progresschannel}]} {
set opt_progresschannel ""
}
if {$CALLDEPTH == 0} { if {$CALLDEPTH == 0} {
#expensive to normalize but we need to do it at least once #expensive to normalize but we need to do it at least once
@ -1485,6 +1516,7 @@ namespace eval punkcheck {
dict unset config -call-depth-internal dict unset config -call-depth-internal
dict unset config -max_depth dict unset config -max_depth
dict unset config -subdirlist dict unset config -subdirlist
dict unset config -progresschannel
tcl::dict::for {k v} $config { tcl::dict::for {k v} $config {
if {$v eq "\uFFFF"} { if {$v eq "\uFFFF"} {
dict unset config $k dict unset config $k
@ -1602,7 +1634,7 @@ namespace eval punkcheck {
#puts stdout "Current target dir: $current_target_dir" #puts stdout "Current target dir: $current_target_dir"
set last_processed_dir "" set last_depth ""
foreach m $match_list { foreach m $match_list {
set new_tgt_cksum_info [list] set new_tgt_cksum_info [list]
set relative_target_path [file join $relative_target_dir $m] set relative_target_path [file join $relative_target_dir $m]
@ -1620,23 +1652,6 @@ namespace eval punkcheck {
if {$is_antipath} { if {$is_antipath} {
continue continue
} }
#puts stdout " checking file : $current_source_dir/$m"
#give some output - but not too deep
#set thismatchdir [file dirname [file dirname [file join $srcdir $relative_source_dir]]]
#if {$last_processed_dir ne $thismatchdir} {
# puts stdout "\n checking files in $thismatchdir"
# set last_processed_dir $thismatchdir
#} else {
# puts -nonewline stdout .
#}
if {$last_processed_dir ne $CALLDEPTH} {
puts -nonewline stdout \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
puts -nonewline stdout \n[string repeat " " $CALLDEPTH].
flush stdout
set last_processed_dir $CALLDEPTH
} else {
puts -nonewline stdout .
}
set ts_start [clock microseconds] set ts_start [clock microseconds]
set seconds [expr {$ts_start / 1000000}] set seconds [expr {$ts_start / 1000000}]
@ -1649,7 +1664,9 @@ namespace eval punkcheck {
#change to use extract_or_create_fileset_record ? #change to use extract_or_create_fileset_record ?
set existing_filerec_posn [dict get $fetch_filerec_result position] set existing_filerec_posn [dict get $fetch_filerec_result position]
if {$existing_filerec_posn == -1} { if {$existing_filerec_posn == -1} {
puts stdout "NO existing record for $punkcheck_target_relpath" if {$opt_progresschannel ne ""} {
puts stdout "\nNO existing record for $punkcheck_target_relpath"
}
set has_filerec 0 set has_filerec 0
set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath] set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath]
set filerec $new_filerec set filerec $new_filerec
@ -1667,14 +1684,24 @@ namespace eval punkcheck {
unset new_install_record unset new_install_record
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
if {$CALLDEPTH <=1} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
}
flush $opt_progresschannel
##set last_depth $CALLDEPTH ;# done down below
}
}
set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m] set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m]
#puts stdout " rel_source: $relative_source_path" #puts stdout " rel_source: $relative_source_path"
if {[file pathtype $relative_source_path] ne "relative"} { #if {[file pathtype $relative_source_path] ne "relative"} {
#REVIEW
#different volume or root #different volume or root
} #}
#Note this isn't a recordlist function - so it doesn't purely operate on the records #Note this isn't a recordlist function - so it doesn't purely operate on the records
#this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config. #this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config.
#It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't) #It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't)
@ -1683,6 +1710,7 @@ namespace eval punkcheck {
set ts2 [clock milliseconds] set ts2 [clock milliseconds]
set diff [expr {$ts2 - $ts1}] set diff [expr {$ts2 - $ts1}]
if {$diff > 100} { if {$diff > 100} {
#todo -errorchannel
set errprefix ">>> punkcheck:" set errprefix ">>> punkcheck:"
puts stderr "\n$errprefix performance warning: fetch_metadata for $m took $diff ms." puts stderr "\n$errprefix performance warning: fetch_metadata for $m took $diff ms."
set lb [lindex [dict get $filerec body] end] set lb [lindex [dict get $filerec body] end]
@ -1710,6 +1738,7 @@ namespace eval punkcheck {
} }
set is_skip 0 set is_skip 0
set is_new 0
if {$overwrite_what eq "all-targets"} { if {$overwrite_what eq "all-targets"} {
file mkdir $current_target_dir file mkdir $current_target_dir
#-------------------------------------------- #--------------------------------------------
@ -1720,12 +1749,13 @@ namespace eval punkcheck {
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
} else { } else {
if {![file exists $current_target_dir/$m]} { if {![file exists $current_target_dir/$m]} {
puts stderr "punkcheck: first copy to $current_target_dir/$m " #puts stderr "punkcheck: first copy to $current_target_dir/$m "
file mkdir $current_target_dir file mkdir $current_target_dir
file copy $current_source_dir/$m $current_target_dir file copy $current_source_dir/$m $current_target_dir
set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
incr filecount_new incr filecount_new
set is_new 1
} else { } else {
switch -- $overwrite_what { switch -- $overwrite_what {
installedsourcechanged-targets { installedsourcechanged-targets {
@ -1747,14 +1777,16 @@ namespace eval punkcheck {
} }
} }
synced-targets { synced-targets {
#disallow overwriting of target that has been modified by some other mechanism
#review
if {[llength $changed]} { if {[llength $changed]} {
#only overwrite if the target checksum equals the last installed checksum (ie target is in sync with source and so hasn't been customized) #only overwrite if the target file checksum equals the last installed checksum (ie target is in sync with previous source-set and so hasn't been customized)
set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
set is_target_unmodified_since_install 0 set is_target_unmodified_since_install 0
set target_cksum_compare "unknown" set target_cksum_compare "unknown"
set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list
if {[dict exists $latest_install_record -targets_cksums]} { if {[dict exists $latest_install_record -targets_cksums]} {
set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder) set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder)
if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} { if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} {
set is_target_unmodified_since_install 1 set is_target_unmodified_since_install 1
set target_cksum_compare "match" set target_cksum_compare "match"
@ -1797,6 +1829,7 @@ namespace eval punkcheck {
set ts_now [clock microseconds] set ts_now [clock microseconds]
set elapsed_us [expr {$ts_now - $ts_start}] set elapsed_us [expr {$ts_now - $ts_start}]
@ -1827,6 +1860,29 @@ namespace eval punkcheck {
lset punkcheck_records $existing_filerec_posn $filerec lset punkcheck_records $existing_filerec_posn $filerec
} }
#------------------------------------------------------------
if {$is_skip} {
set mark .
} else {
if {$is_new} {
set mark \x1b\[32\;1mN\x1b\[m
} else {
#updated
set mark \x1b\[32\;1mU\x1b\[m
}
}
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH]$mark
flush $opt_progresschannel
set last_depth $CALLDEPTH
} else {
puts -nonewline $opt_progresschannel $mark
}
}
#------------------------------------------------------------
} }
if {$max_depth != -1 && $CALLDEPTH >= $max_depth} { if {$max_depth != -1 && $CALLDEPTH >= $max_depth} {
@ -1905,6 +1961,7 @@ namespace eval punkcheck {
-punkcheck_folder $punkcheck_folder\ -punkcheck_folder $punkcheck_folder\
-punkcheck_eventid $punkcheck_eventid\ -punkcheck_eventid $punkcheck_eventid\
-punkcheck_records $punkcheck_records\ -punkcheck_records $punkcheck_records\
-progresschannel $opt_progresschannel\
] ]
set sub_opts [dict merge $opts $sub_opts] set sub_opts [dict merge $opts $sub_opts]
set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts] set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts]

47
src/modules/textblock-999999.0a1.0.tm

@ -60,6 +60,7 @@ package require punk::ansi
package require punk::lib package require punk::lib
catch {package require patternpunk} catch {package require patternpunk}
package require overtype package require overtype
package require struct::set
#safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list. #safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list.
#2025 - required term::ansi features for altg now built in to textblock #2025 - required term::ansi features for altg now built in to textblock
@ -2533,24 +2534,26 @@ tcl::namespace::eval textblock {
set parts [punk::ansi::ta::split_codes_single $c] set parts [punk::ansi::ta::split_codes_single $c]
#we have detected ansi - so there will always be at least 3 parts beginning and ending with pt pt,ansi,pt,ansi...,pt #we have detected ansi - so there will always be at least 3 parts beginning and ending with pt pt,ansi,pt,ansi...,pt
set codes [list] set codes [list]
set ptlens [list]
foreach {pt cd} $parts { foreach {pt cd} $parts {
if {$cd ne ""} { lappend codes $cd
lappend codes $cd lappend ptlens [string length $pt]
}
} }
#set takebg [lindex $parts end-1] #set takebg [lindex $parts end-1]
#set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1] #set cell_bg [punk::ansi::codetype::sgr_merge_singles [list $takebg] -filter_fg 1]
set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1 -filter_reset 1] set cell_bg [punk::ansi::codetype::sgr_merge_singles $codes -filter_fg 1 -filter_reset 1]
#puts --->[ansistring VIEW $codes] #puts --->[ansistring VIEW $codes]
if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end]]} { if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} {
if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} { if {[lindex $ptlens end] == 0 && [lindex $ptlens end-1] == 0 && [punk::ansi::codetype::is_sgr_reset [lindex $codes end-2]]} {
#special case double reset at end of content #special case double reset at end of content
set cell_ansi_tail [punk::ansi::codetype::sgr_merge_singles $codes] ;#no filters #TODO - write test!
set cell_ansi_tail [punk::ansi::codetype::sgr_merge_singles [lrange $codes 0 end-3]] ;#no filters
set ansibase "" set ansibase ""
set row_ansibase "" set row_ansibase ""
if {$ftblock} { if {$ftblock} {
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1] set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_final $cell_bg]]
} }
set cell_ansibase $cell_ansi_tail set cell_ansibase $cell_ansi_tail
} else { } else {
@ -2564,6 +2567,28 @@ tcl::namespace::eval textblock {
} }
set cell_ansibase $cell_bg set cell_ansibase $cell_bg
} }
#v1
#if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end]]} {
# if {[punk::ansi::codetype::is_sgr_reset [lindex $codes end-1]]} {
# #special case double reset at end of content
# set cell_ansi_tail [punk::ansi::codetype::sgr_merge_singles $codes] ;#no filters
# set ansibase ""
# set row_ansibase ""
# if {$ftblock} {
# set ansiborder_final [punk::ansi::codetype::sgr_merge [list $ansiborder_body_col_row] -filter_bg 1]
# }
# set cell_ansibase $cell_ansi_tail
# } else {
# #single trailing reset in content
# set cell_ansibase "" ;#cell doesn't contribute to frame's ansibase
# }
#} else {
# if {$ftblock} {
# #no resets - use cell's bg to extend to the border - only for block frames
# set ansiborder_final $ansiborder_body_col_row$cell_bg
# }
# set cell_ansibase $cell_bg
#}
} }
set ansibase_final $ansibase$row_ansibase$cell_ansibase set ansibase_final $ansibase$row_ansibase$cell_ansibase
@ -8628,8 +8653,11 @@ tcl::namespace::eval textblock {
if {$opt_ansibase ne ""} { if {$opt_ansibase ne ""} {
if {[punk::ansi::ta::detect $cache_inner]} { if {[punk::ansi::ta::detect $cache_inner]} {
#set cache_inner [punk::ansi::ansiwrap -rawansi $opt_ansibase $cache_inner] #set cache_inner [punk::ansi::ansiwrap -rawansi $opt_ansibase $cache_inner]
set cache_inner [punk::ansi::ansiwrap_raw $opt_ansibase "" "" $cache_inner] #set cache_inner [punk::ansi::ansiwrap_raw $opt_ansibase "" "" $cache_inner]
#jjj ??? review
set cache_inner [punk::ansi::ansiwrap_raw $opt_ansibase "\x1b\[m" "" $cache_inner]
} else { } else {
#!!!
set cache_inner "$opt_ansibase$cache_inner\x1b\[0m" set cache_inner "$opt_ansibase$cache_inner\x1b\[0m"
} }
} }
@ -8722,7 +8750,10 @@ tcl::namespace::eval textblock {
if {$opt_ansibase ne ""} { if {$opt_ansibase ne ""} {
if {$contents_has_ansi} { if {$contents_has_ansi} {
#set contents [punk::ansi::ansiwrap -rawansi $opt_ansibase $contents] #set contents [punk::ansi::ansiwrap -rawansi $opt_ansibase $contents]
set contents [punk::ansi::ansiwrap_raw $opt_ansibase "" "" $contents] #set contents [punk::ansi::ansiwrap_raw $opt_ansibase "" "" $contents]
#jjj
set contents [punk::ansi::ansiwrap_raw $opt_ansibase "\x1b\[m" "" $contents]
#set contents "$opt_ansibase$contents\x1b\[0m"
} else { } else {
set contents "$opt_ansibase$contents\x1b\[0m" set contents "$opt_ansibase$contents\x1b\[0m"
set contents_has_ansi 1 set contents_has_ansi 1

34
src/project_layouts/custom/_project/punk.basic/src/make.tcl

@ -1653,7 +1653,7 @@ if {$::punkboot::command eq "bootsupport"} {
file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists
$boot_event targetset_started $boot_event targetset_started
# -- --- --- --- --- --- # -- --- --- --- --- ---
puts "BOOTSUPPORT module$which update: $srcfile -> $tgtfile" puts "\nBOOTSUPPORT module$which update: $srcfile -> $tgtfile"
if {[catch { if {[catch {
file copy -force $srcfile $tgtfile file copy -force $srcfile $tgtfile
} errM]} { } errM]} {
@ -1719,7 +1719,8 @@ if {$::punkboot::command eq "bootsupport"} {
set resultdict [punkcheck::install $sourcemodules $targetroot\ set resultdict [punkcheck::install $sourcemodules $targetroot\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\ -antiglob_paths $antipaths\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
@ -1737,7 +1738,8 @@ if {$::punkboot::command eq "bootsupport"} {
-max_depth 1\ -max_depth 1\
-createempty 0\ -createempty 0\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
flush stdout flush stdout
@ -1784,7 +1786,12 @@ if {$::punkboot::command in {project modules}} {
#install .tm *and other files* #install .tm *and other files*
puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)" puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder -installer make.tcl -overwrite installedsourcechanged-targets -antiglob_paths {README.md include_modules.config}] set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder\
-installer make.tcl\
-overwrite installedsourcechanged-targets\
-antiglob_paths {README.md include_modules.config}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendormodulefolders]} { if {![llength $vendormodulefolders]} {
@ -1810,7 +1817,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
@ -1906,7 +1917,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $projectlibfolders]} { if {![llength $projectlibfolders]} {
@ -1941,7 +1956,12 @@ if {$::punkboot::command in {project modules}} {
set overwrite "installedsourcechanged-targets" set overwrite "installedsourcechanged-targets"
#set overwrite "ALL-TARGETS" #set overwrite "ALL-TARGETS"
puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)" puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)"
set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base -installer make.tcl -overwrite $overwrite -antiglob_paths {README.md}] set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base\
-installer make.tcl\
-overwrite $overwrite\
-antiglob_paths {README.md}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }

6
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.tm

@ -4468,13 +4468,13 @@ tcl::namespace::eval punk::args {
#error "punk::args::parse Even number of -flag val pairs required after arglist" #error "punk::args::parse Even number of -flag val pairs required after arglist"
#} #}
#Default the -errorstyle to enhanced #Default the -errorstyle to standard
# (slowest on unhappy path - but probably clearest for playing with new APIs interactively) # (slow on unhappy path - but probably clearest for playing with new APIs interactively)
# - application devs should distribute a config file with an errorstyle override if desired. # - application devs should distribute a config file with an errorstyle override if desired.
# - devs who prefer a different default for interactive use should create a config for it. (todo) # - devs who prefer a different default for interactive use should create a config for it. (todo)
set defaultopts [dict create\ set defaultopts [dict create\
-form {*}\ -form {*}\
-errorstyle enhanced\ -errorstyle standard\
] ]
#todo - load override_errorstyle from configuration #todo - load override_errorstyle from configuration

129
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punkcheck-0.1.0.tm

@ -32,9 +32,16 @@ package require punk::mix::util
#see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113 #see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113
# #
namespace eval punkcheck { namespace eval punkcheck {
#namespace export\
# uuid\
# start_installer_event installfile_*
namespace export\ namespace export\
uuid\ uuid\
start_installer_event installfile_* installtrack\
install\
install_tm_files\
install_non_tm_files\
summarize_install_resultdict\
#antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators #antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators
variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"] variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"]
@ -48,7 +55,7 @@ namespace eval punkcheck {
} }
if {!$has_twapi} { if {!$has_twapi} {
if {[catch {package require uuid} errM]} { if {[catch {package require uuid} errM]} {
error "Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows" error "punkcheck: Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows"
} }
return [uuid::uuid generate] return [uuid::uuid generate]
} else { } else {
@ -150,6 +157,8 @@ namespace eval punkcheck {
} }
} }
#instances created by an installtrack object in method start_event
#also in installtrack constructor - to represent existing events from the .punkcheck data
oo::class create installevent { oo::class create installevent {
variable o_id variable o_id
variable o_rel_sourceroot variable o_rel_sourceroot
@ -266,7 +275,7 @@ namespace eval punkcheck {
set o_operation [string toupper $operation] set o_operation [string toupper $operation]
if {$o_operation_start_ts ne ""} { if {$o_operation_start_ts ne ""} {
error "[self] targetset_tart $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish." error "[self] targetset_init $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish."
} }
set o_operation_start_ts [clock microseconds] set o_operation_start_ts [clock microseconds]
set seconds [expr {$o_operation_start_ts / 1000000}] set seconds [expr {$o_operation_start_ts / 1000000}]
@ -1241,7 +1250,17 @@ namespace eval punkcheck {
lappend PUNKARGS [list { lappend PUNKARGS [list {
@id -id ::punkcheck::install @id -id ::punkcheck::install
@cmd -name ::punkcheck::install -help\ @cmd -name ::punkcheck::install -help\
"Unidirectional file transfer to possibly non-empty target folder." "Unidirectional file transfer to possibly non-empty target folder.
This is the simpler form of the API, performing a transfer from one
directory tree to another, copying each file when changes in the source
file are detected.
Changes are detected by content checksum. The first install will record
source checksums in a .punkcheck file (ideally located at the root of the
target folder). Subsequent installs will compare stored checksums with
the current checksums of the source files.
For more advanced install operations, the object command installtrack
can be used to define install operations. e.g when the transfer is not
one-to-one and a target file depends on multiple source files."
@leaders -min 2 -max 2 @leaders -min 2 -max 2
srcdir -type directory srcdir -type directory
tgtdir -type directory tgtdir -type directory
@ -1251,7 +1270,7 @@ namespace eval punkcheck {
"Deepest subdirectory - use -1 for no limit." "Deepest subdirectory - use -1 for no limit."
-createdir -type boolean -default 0 -help\ -createdir -type boolean -default 0 -help\
"Whether to create the folder at tgtdir. "Whether to create the folder at tgtdir.
Any required subdirectories are created regardless of this setting." Any required subdirectories are created regardless of this setting."
-createempty -type boolean -default 0 -help\ -createempty -type boolean -default 0 -help\
"Whether to create folders at target that had no matches for our glob" "Whether to create folders at target that had no matches for our glob"
-glob -type string -default "*" -help\ -glob -type string -default "*" -help\
@ -1282,8 +1301,8 @@ namespace eval punkcheck {
} }
-punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\ -punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\
"The location of the .punkcheck file to track installations and checksums. "The location of the .punkcheck file to track installations and checksums.
The default value 'target' is generally recommended. The default value 'target' is generally recommended.
Can also be an absolute path to a folder." Can also be an absolute path to a folder."
-punkcheck_records -default "" -help\ -punkcheck_records -default "" -help\
"Empty string or a parsed TDL records structure. "Empty string or a parsed TDL records structure.
e.g e.g
@ -1294,7 +1313,14 @@ namespace eval punkcheck {
}" }"
-installer -default "punkcheck::install" -help\ -installer -default "punkcheck::install" -help\
"A user nominated string that is stored in the .punkcheck file "A user nominated string that is stored in the .punkcheck file
This might be the name of a script or installation process." This might be the name of a script or installation process."
-progresschannel -default none -type string -help\
"Name of channel e.g stderr, stdout to which progress messages are written.
This includes the tree-like output consisting of dots (or green U) for each
file processed. As the number of files in a tree is not known beforehand,
it isn't useful for a percentage-based progress meter, but it could potentially
be used to drive a spinner if the textual data is not desired.
Setting to none or an invalid channel will deactivate the output."
}] }]
## unidirectional file transfer to possibly non empty folder ## unidirectional file transfer to possibly non empty folder
#default of -overwrite no-targets will only copy files that are missing at the target #default of -overwrite no-targets will only copy files that are missing at the target
@ -1343,6 +1369,7 @@ namespace eval punkcheck {
-punkcheck_eventid "\uFFFF"\ -punkcheck_eventid "\uFFFF"\
-punkcheck_records ""\ -punkcheck_records ""\
-installer punkcheck::install\ -installer punkcheck::install\
-progresschannel none\
] ]
if {([llength $args] %2) != 0} { if {([llength $args] %2) != 0} {
@ -1367,6 +1394,10 @@ namespace eval punkcheck {
set fileglob [dict get $opts -glob] set fileglob [dict get $opts -glob]
set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting
set opt_createempty [dict get $opts -createempty] set opt_createempty [dict get $opts -createempty]
set opt_progresschannel [dict get $opts -progresschannel]
if {$opt_progresschannel in {"" none} || [catch {chan configure $opt_progresschannel}]} {
set opt_progresschannel ""
}
if {$CALLDEPTH == 0} { if {$CALLDEPTH == 0} {
#expensive to normalize but we need to do it at least once #expensive to normalize but we need to do it at least once
@ -1485,6 +1516,7 @@ namespace eval punkcheck {
dict unset config -call-depth-internal dict unset config -call-depth-internal
dict unset config -max_depth dict unset config -max_depth
dict unset config -subdirlist dict unset config -subdirlist
dict unset config -progresschannel
tcl::dict::for {k v} $config { tcl::dict::for {k v} $config {
if {$v eq "\uFFFF"} { if {$v eq "\uFFFF"} {
dict unset config $k dict unset config $k
@ -1602,7 +1634,7 @@ namespace eval punkcheck {
#puts stdout "Current target dir: $current_target_dir" #puts stdout "Current target dir: $current_target_dir"
set last_processed_dir "" set last_depth ""
foreach m $match_list { foreach m $match_list {
set new_tgt_cksum_info [list] set new_tgt_cksum_info [list]
set relative_target_path [file join $relative_target_dir $m] set relative_target_path [file join $relative_target_dir $m]
@ -1620,23 +1652,6 @@ namespace eval punkcheck {
if {$is_antipath} { if {$is_antipath} {
continue continue
} }
#puts stdout " checking file : $current_source_dir/$m"
#give some output - but not too deep
#set thismatchdir [file dirname [file dirname [file join $srcdir $relative_source_dir]]]
#if {$last_processed_dir ne $thismatchdir} {
# puts stdout "\n checking files in $thismatchdir"
# set last_processed_dir $thismatchdir
#} else {
# puts -nonewline stdout .
#}
if {$last_processed_dir ne $CALLDEPTH} {
puts -nonewline stdout \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
puts -nonewline stdout \n[string repeat " " $CALLDEPTH].
flush stdout
set last_processed_dir $CALLDEPTH
} else {
puts -nonewline stdout .
}
set ts_start [clock microseconds] set ts_start [clock microseconds]
set seconds [expr {$ts_start / 1000000}] set seconds [expr {$ts_start / 1000000}]
@ -1649,7 +1664,9 @@ namespace eval punkcheck {
#change to use extract_or_create_fileset_record ? #change to use extract_or_create_fileset_record ?
set existing_filerec_posn [dict get $fetch_filerec_result position] set existing_filerec_posn [dict get $fetch_filerec_result position]
if {$existing_filerec_posn == -1} { if {$existing_filerec_posn == -1} {
puts stdout "NO existing record for $punkcheck_target_relpath" if {$opt_progresschannel ne ""} {
puts stdout "\nNO existing record for $punkcheck_target_relpath"
}
set has_filerec 0 set has_filerec 0
set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath] set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath]
set filerec $new_filerec set filerec $new_filerec
@ -1667,14 +1684,24 @@ namespace eval punkcheck {
unset new_install_record unset new_install_record
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
if {$CALLDEPTH <=1} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
}
flush $opt_progresschannel
##set last_depth $CALLDEPTH ;# done down below
}
}
set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m] set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m]
#puts stdout " rel_source: $relative_source_path" #puts stdout " rel_source: $relative_source_path"
if {[file pathtype $relative_source_path] ne "relative"} { #if {[file pathtype $relative_source_path] ne "relative"} {
#REVIEW
#different volume or root #different volume or root
} #}
#Note this isn't a recordlist function - so it doesn't purely operate on the records #Note this isn't a recordlist function - so it doesn't purely operate on the records
#this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config. #this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config.
#It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't) #It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't)
@ -1683,18 +1710,19 @@ namespace eval punkcheck {
set ts2 [clock milliseconds] set ts2 [clock milliseconds]
set diff [expr {$ts2 - $ts1}] set diff [expr {$ts2 - $ts1}]
if {$diff > 100} { if {$diff > 100} {
#todo -errorchannel
set errprefix ">>> punkcheck:" set errprefix ">>> punkcheck:"
puts stderr "$errprefix performance warning: fetch_metadata for $m took $diff ms." puts stderr "\n$errprefix performance warning: fetch_metadata for $m took $diff ms."
set lb [lindex [dict get $filerec body] end] set lb [lindex [dict get $filerec body] end]
#puts stderr "$errprefix filerec last body record:$lb" #puts stderr "$errprefix filerec last body record:$lb"
set records [dict get $lb body] set records [dict get $lb body]
set lr [lindex $records end] set lr [lindex $records end]
set alg [dict get $lr -cksum_all_opts -cksum_algorithm] set alg [dict get $lr -cksum_all_opts -cksum_algorithm]
if {$alg eq "sha1"} { if {$alg eq "sha1"} {
puts "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])" puts stderr "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])"
puts "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]" puts stderr "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]"
} else { } else {
puts "$errprefix cksum_algorithm: $alg" puts stderr "$errprefix cksum_algorithm: $alg"
} }
} }
@ -1710,6 +1738,7 @@ namespace eval punkcheck {
} }
set is_skip 0 set is_skip 0
set is_new 0
if {$overwrite_what eq "all-targets"} { if {$overwrite_what eq "all-targets"} {
file mkdir $current_target_dir file mkdir $current_target_dir
#-------------------------------------------- #--------------------------------------------
@ -1720,12 +1749,13 @@ namespace eval punkcheck {
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
} else { } else {
if {![file exists $current_target_dir/$m]} { if {![file exists $current_target_dir/$m]} {
puts stderr "punkcheck: first copy to $current_target_dir/$m " #puts stderr "punkcheck: first copy to $current_target_dir/$m "
file mkdir $current_target_dir file mkdir $current_target_dir
file copy $current_source_dir/$m $current_target_dir file copy $current_source_dir/$m $current_target_dir
set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
incr filecount_new incr filecount_new
set is_new 1
} else { } else {
switch -- $overwrite_what { switch -- $overwrite_what {
installedsourcechanged-targets { installedsourcechanged-targets {
@ -1747,14 +1777,16 @@ namespace eval punkcheck {
} }
} }
synced-targets { synced-targets {
#disallow overwriting of target that has been modified by some other mechanism
#review
if {[llength $changed]} { if {[llength $changed]} {
#only overwrite if the target checksum equals the last installed checksum (ie target is in sync with source and so hasn't been customized) #only overwrite if the target file checksum equals the last installed checksum (ie target is in sync with previous source-set and so hasn't been customized)
set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
set is_target_unmodified_since_install 0 set is_target_unmodified_since_install 0
set target_cksum_compare "unknown" set target_cksum_compare "unknown"
set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list
if {[dict exists $latest_install_record -targets_cksums]} { if {[dict exists $latest_install_record -targets_cksums]} {
set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder) set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder)
if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} { if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} {
set is_target_unmodified_since_install 1 set is_target_unmodified_since_install 1
set target_cksum_compare "match" set target_cksum_compare "match"
@ -1797,6 +1829,7 @@ namespace eval punkcheck {
set ts_now [clock microseconds] set ts_now [clock microseconds]
set elapsed_us [expr {$ts_now - $ts_start}] set elapsed_us [expr {$ts_now - $ts_start}]
@ -1827,6 +1860,29 @@ namespace eval punkcheck {
lset punkcheck_records $existing_filerec_posn $filerec lset punkcheck_records $existing_filerec_posn $filerec
} }
#------------------------------------------------------------
if {$is_skip} {
set mark .
} else {
if {$is_new} {
set mark \x1b\[32\;1mN\x1b\[m
} else {
#updated
set mark \x1b\[32\;1mU\x1b\[m
}
}
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH]$mark
flush $opt_progresschannel
set last_depth $CALLDEPTH
} else {
puts -nonewline $opt_progresschannel $mark
}
}
#------------------------------------------------------------
} }
if {$max_depth != -1 && $CALLDEPTH >= $max_depth} { if {$max_depth != -1 && $CALLDEPTH >= $max_depth} {
@ -1905,6 +1961,7 @@ namespace eval punkcheck {
-punkcheck_folder $punkcheck_folder\ -punkcheck_folder $punkcheck_folder\
-punkcheck_eventid $punkcheck_eventid\ -punkcheck_eventid $punkcheck_eventid\
-punkcheck_records $punkcheck_records\ -punkcheck_records $punkcheck_records\
-progresschannel $opt_progresschannel\
] ]
set sub_opts [dict merge $opts $sub_opts] set sub_opts [dict merge $opts $sub_opts]
set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts] set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts]

1
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm

@ -60,6 +60,7 @@ package require punk::ansi
package require punk::lib package require punk::lib
catch {package require patternpunk} catch {package require patternpunk}
package require overtype package require overtype
package require struct::set
#safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list. #safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list.
#2025 - required term::ansi features for altg now built in to textblock #2025 - required term::ansi features for altg now built in to textblock

34
src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl

@ -1653,7 +1653,7 @@ if {$::punkboot::command eq "bootsupport"} {
file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists
$boot_event targetset_started $boot_event targetset_started
# -- --- --- --- --- --- # -- --- --- --- --- ---
puts "BOOTSUPPORT module$which update: $srcfile -> $tgtfile" puts "\nBOOTSUPPORT module$which update: $srcfile -> $tgtfile"
if {[catch { if {[catch {
file copy -force $srcfile $tgtfile file copy -force $srcfile $tgtfile
} errM]} { } errM]} {
@ -1719,7 +1719,8 @@ if {$::punkboot::command eq "bootsupport"} {
set resultdict [punkcheck::install $sourcemodules $targetroot\ set resultdict [punkcheck::install $sourcemodules $targetroot\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\ -antiglob_paths $antipaths\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
@ -1737,7 +1738,8 @@ if {$::punkboot::command eq "bootsupport"} {
-max_depth 1\ -max_depth 1\
-createempty 0\ -createempty 0\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
flush stdout flush stdout
@ -1784,7 +1786,12 @@ if {$::punkboot::command in {project modules}} {
#install .tm *and other files* #install .tm *and other files*
puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)" puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder -installer make.tcl -overwrite installedsourcechanged-targets -antiglob_paths {README.md include_modules.config}] set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder\
-installer make.tcl\
-overwrite installedsourcechanged-targets\
-antiglob_paths {README.md include_modules.config}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendormodulefolders]} { if {![llength $vendormodulefolders]} {
@ -1810,7 +1817,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
@ -1906,7 +1917,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $projectlibfolders]} { if {![llength $projectlibfolders]} {
@ -1941,7 +1956,12 @@ if {$::punkboot::command in {project modules}} {
set overwrite "installedsourcechanged-targets" set overwrite "installedsourcechanged-targets"
#set overwrite "ALL-TARGETS" #set overwrite "ALL-TARGETS"
puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)" puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)"
set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base -installer make.tcl -overwrite $overwrite -antiglob_paths {README.md}] set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base\
-installer make.tcl\
-overwrite $overwrite\
-antiglob_paths {README.md}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }

6
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.tm

@ -4468,13 +4468,13 @@ tcl::namespace::eval punk::args {
#error "punk::args::parse Even number of -flag val pairs required after arglist" #error "punk::args::parse Even number of -flag val pairs required after arglist"
#} #}
#Default the -errorstyle to enhanced #Default the -errorstyle to standard
# (slowest on unhappy path - but probably clearest for playing with new APIs interactively) # (slow on unhappy path - but probably clearest for playing with new APIs interactively)
# - application devs should distribute a config file with an errorstyle override if desired. # - application devs should distribute a config file with an errorstyle override if desired.
# - devs who prefer a different default for interactive use should create a config for it. (todo) # - devs who prefer a different default for interactive use should create a config for it. (todo)
set defaultopts [dict create\ set defaultopts [dict create\
-form {*}\ -form {*}\
-errorstyle enhanced\ -errorstyle standard\
] ]
#todo - load override_errorstyle from configuration #todo - load override_errorstyle from configuration

129
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punkcheck-0.1.0.tm

@ -32,9 +32,16 @@ package require punk::mix::util
#see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113 #see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113
# #
namespace eval punkcheck { namespace eval punkcheck {
#namespace export\
# uuid\
# start_installer_event installfile_*
namespace export\ namespace export\
uuid\ uuid\
start_installer_event installfile_* installtrack\
install\
install_tm_files\
install_non_tm_files\
summarize_install_resultdict\
#antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators #antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators
variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"] variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"]
@ -48,7 +55,7 @@ namespace eval punkcheck {
} }
if {!$has_twapi} { if {!$has_twapi} {
if {[catch {package require uuid} errM]} { if {[catch {package require uuid} errM]} {
error "Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows" error "punkcheck: Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows"
} }
return [uuid::uuid generate] return [uuid::uuid generate]
} else { } else {
@ -150,6 +157,8 @@ namespace eval punkcheck {
} }
} }
#instances created by an installtrack object in method start_event
#also in installtrack constructor - to represent existing events from the .punkcheck data
oo::class create installevent { oo::class create installevent {
variable o_id variable o_id
variable o_rel_sourceroot variable o_rel_sourceroot
@ -266,7 +275,7 @@ namespace eval punkcheck {
set o_operation [string toupper $operation] set o_operation [string toupper $operation]
if {$o_operation_start_ts ne ""} { if {$o_operation_start_ts ne ""} {
error "[self] targetset_tart $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish." error "[self] targetset_init $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish."
} }
set o_operation_start_ts [clock microseconds] set o_operation_start_ts [clock microseconds]
set seconds [expr {$o_operation_start_ts / 1000000}] set seconds [expr {$o_operation_start_ts / 1000000}]
@ -1241,7 +1250,17 @@ namespace eval punkcheck {
lappend PUNKARGS [list { lappend PUNKARGS [list {
@id -id ::punkcheck::install @id -id ::punkcheck::install
@cmd -name ::punkcheck::install -help\ @cmd -name ::punkcheck::install -help\
"Unidirectional file transfer to possibly non-empty target folder." "Unidirectional file transfer to possibly non-empty target folder.
This is the simpler form of the API, performing a transfer from one
directory tree to another, copying each file when changes in the source
file are detected.
Changes are detected by content checksum. The first install will record
source checksums in a .punkcheck file (ideally located at the root of the
target folder). Subsequent installs will compare stored checksums with
the current checksums of the source files.
For more advanced install operations, the object command installtrack
can be used to define install operations. e.g when the transfer is not
one-to-one and a target file depends on multiple source files."
@leaders -min 2 -max 2 @leaders -min 2 -max 2
srcdir -type directory srcdir -type directory
tgtdir -type directory tgtdir -type directory
@ -1251,7 +1270,7 @@ namespace eval punkcheck {
"Deepest subdirectory - use -1 for no limit." "Deepest subdirectory - use -1 for no limit."
-createdir -type boolean -default 0 -help\ -createdir -type boolean -default 0 -help\
"Whether to create the folder at tgtdir. "Whether to create the folder at tgtdir.
Any required subdirectories are created regardless of this setting." Any required subdirectories are created regardless of this setting."
-createempty -type boolean -default 0 -help\ -createempty -type boolean -default 0 -help\
"Whether to create folders at target that had no matches for our glob" "Whether to create folders at target that had no matches for our glob"
-glob -type string -default "*" -help\ -glob -type string -default "*" -help\
@ -1282,8 +1301,8 @@ namespace eval punkcheck {
} }
-punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\ -punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\
"The location of the .punkcheck file to track installations and checksums. "The location of the .punkcheck file to track installations and checksums.
The default value 'target' is generally recommended. The default value 'target' is generally recommended.
Can also be an absolute path to a folder." Can also be an absolute path to a folder."
-punkcheck_records -default "" -help\ -punkcheck_records -default "" -help\
"Empty string or a parsed TDL records structure. "Empty string or a parsed TDL records structure.
e.g e.g
@ -1294,7 +1313,14 @@ namespace eval punkcheck {
}" }"
-installer -default "punkcheck::install" -help\ -installer -default "punkcheck::install" -help\
"A user nominated string that is stored in the .punkcheck file "A user nominated string that is stored in the .punkcheck file
This might be the name of a script or installation process." This might be the name of a script or installation process."
-progresschannel -default none -type string -help\
"Name of channel e.g stderr, stdout to which progress messages are written.
This includes the tree-like output consisting of dots (or green U) for each
file processed. As the number of files in a tree is not known beforehand,
it isn't useful for a percentage-based progress meter, but it could potentially
be used to drive a spinner if the textual data is not desired.
Setting to none or an invalid channel will deactivate the output."
}] }]
## unidirectional file transfer to possibly non empty folder ## unidirectional file transfer to possibly non empty folder
#default of -overwrite no-targets will only copy files that are missing at the target #default of -overwrite no-targets will only copy files that are missing at the target
@ -1343,6 +1369,7 @@ namespace eval punkcheck {
-punkcheck_eventid "\uFFFF"\ -punkcheck_eventid "\uFFFF"\
-punkcheck_records ""\ -punkcheck_records ""\
-installer punkcheck::install\ -installer punkcheck::install\
-progresschannel none\
] ]
if {([llength $args] %2) != 0} { if {([llength $args] %2) != 0} {
@ -1367,6 +1394,10 @@ namespace eval punkcheck {
set fileglob [dict get $opts -glob] set fileglob [dict get $opts -glob]
set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting
set opt_createempty [dict get $opts -createempty] set opt_createempty [dict get $opts -createempty]
set opt_progresschannel [dict get $opts -progresschannel]
if {$opt_progresschannel in {"" none} || [catch {chan configure $opt_progresschannel}]} {
set opt_progresschannel ""
}
if {$CALLDEPTH == 0} { if {$CALLDEPTH == 0} {
#expensive to normalize but we need to do it at least once #expensive to normalize but we need to do it at least once
@ -1485,6 +1516,7 @@ namespace eval punkcheck {
dict unset config -call-depth-internal dict unset config -call-depth-internal
dict unset config -max_depth dict unset config -max_depth
dict unset config -subdirlist dict unset config -subdirlist
dict unset config -progresschannel
tcl::dict::for {k v} $config { tcl::dict::for {k v} $config {
if {$v eq "\uFFFF"} { if {$v eq "\uFFFF"} {
dict unset config $k dict unset config $k
@ -1602,7 +1634,7 @@ namespace eval punkcheck {
#puts stdout "Current target dir: $current_target_dir" #puts stdout "Current target dir: $current_target_dir"
set last_processed_dir "" set last_depth ""
foreach m $match_list { foreach m $match_list {
set new_tgt_cksum_info [list] set new_tgt_cksum_info [list]
set relative_target_path [file join $relative_target_dir $m] set relative_target_path [file join $relative_target_dir $m]
@ -1620,23 +1652,6 @@ namespace eval punkcheck {
if {$is_antipath} { if {$is_antipath} {
continue continue
} }
#puts stdout " checking file : $current_source_dir/$m"
#give some output - but not too deep
#set thismatchdir [file dirname [file dirname [file join $srcdir $relative_source_dir]]]
#if {$last_processed_dir ne $thismatchdir} {
# puts stdout "\n checking files in $thismatchdir"
# set last_processed_dir $thismatchdir
#} else {
# puts -nonewline stdout .
#}
if {$last_processed_dir ne $CALLDEPTH} {
puts -nonewline stdout \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
puts -nonewline stdout \n[string repeat " " $CALLDEPTH].
flush stdout
set last_processed_dir $CALLDEPTH
} else {
puts -nonewline stdout .
}
set ts_start [clock microseconds] set ts_start [clock microseconds]
set seconds [expr {$ts_start / 1000000}] set seconds [expr {$ts_start / 1000000}]
@ -1649,7 +1664,9 @@ namespace eval punkcheck {
#change to use extract_or_create_fileset_record ? #change to use extract_or_create_fileset_record ?
set existing_filerec_posn [dict get $fetch_filerec_result position] set existing_filerec_posn [dict get $fetch_filerec_result position]
if {$existing_filerec_posn == -1} { if {$existing_filerec_posn == -1} {
puts stdout "NO existing record for $punkcheck_target_relpath" if {$opt_progresschannel ne ""} {
puts stdout "\nNO existing record for $punkcheck_target_relpath"
}
set has_filerec 0 set has_filerec 0
set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath] set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath]
set filerec $new_filerec set filerec $new_filerec
@ -1667,14 +1684,24 @@ namespace eval punkcheck {
unset new_install_record unset new_install_record
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
if {$CALLDEPTH <=1} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
}
flush $opt_progresschannel
##set last_depth $CALLDEPTH ;# done down below
}
}
set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m] set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m]
#puts stdout " rel_source: $relative_source_path" #puts stdout " rel_source: $relative_source_path"
if {[file pathtype $relative_source_path] ne "relative"} { #if {[file pathtype $relative_source_path] ne "relative"} {
#REVIEW
#different volume or root #different volume or root
} #}
#Note this isn't a recordlist function - so it doesn't purely operate on the records #Note this isn't a recordlist function - so it doesn't purely operate on the records
#this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config. #this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config.
#It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't) #It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't)
@ -1683,18 +1710,19 @@ namespace eval punkcheck {
set ts2 [clock milliseconds] set ts2 [clock milliseconds]
set diff [expr {$ts2 - $ts1}] set diff [expr {$ts2 - $ts1}]
if {$diff > 100} { if {$diff > 100} {
#todo -errorchannel
set errprefix ">>> punkcheck:" set errprefix ">>> punkcheck:"
puts stderr "$errprefix performance warning: fetch_metadata for $m took $diff ms." puts stderr "\n$errprefix performance warning: fetch_metadata for $m took $diff ms."
set lb [lindex [dict get $filerec body] end] set lb [lindex [dict get $filerec body] end]
#puts stderr "$errprefix filerec last body record:$lb" #puts stderr "$errprefix filerec last body record:$lb"
set records [dict get $lb body] set records [dict get $lb body]
set lr [lindex $records end] set lr [lindex $records end]
set alg [dict get $lr -cksum_all_opts -cksum_algorithm] set alg [dict get $lr -cksum_all_opts -cksum_algorithm]
if {$alg eq "sha1"} { if {$alg eq "sha1"} {
puts "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])" puts stderr "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])"
puts "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]" puts stderr "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]"
} else { } else {
puts "$errprefix cksum_algorithm: $alg" puts stderr "$errprefix cksum_algorithm: $alg"
} }
} }
@ -1710,6 +1738,7 @@ namespace eval punkcheck {
} }
set is_skip 0 set is_skip 0
set is_new 0
if {$overwrite_what eq "all-targets"} { if {$overwrite_what eq "all-targets"} {
file mkdir $current_target_dir file mkdir $current_target_dir
#-------------------------------------------- #--------------------------------------------
@ -1720,12 +1749,13 @@ namespace eval punkcheck {
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
} else { } else {
if {![file exists $current_target_dir/$m]} { if {![file exists $current_target_dir/$m]} {
puts stderr "punkcheck: first copy to $current_target_dir/$m " #puts stderr "punkcheck: first copy to $current_target_dir/$m "
file mkdir $current_target_dir file mkdir $current_target_dir
file copy $current_source_dir/$m $current_target_dir file copy $current_source_dir/$m $current_target_dir
set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
incr filecount_new incr filecount_new
set is_new 1
} else { } else {
switch -- $overwrite_what { switch -- $overwrite_what {
installedsourcechanged-targets { installedsourcechanged-targets {
@ -1747,14 +1777,16 @@ namespace eval punkcheck {
} }
} }
synced-targets { synced-targets {
#disallow overwriting of target that has been modified by some other mechanism
#review
if {[llength $changed]} { if {[llength $changed]} {
#only overwrite if the target checksum equals the last installed checksum (ie target is in sync with source and so hasn't been customized) #only overwrite if the target file checksum equals the last installed checksum (ie target is in sync with previous source-set and so hasn't been customized)
set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
set is_target_unmodified_since_install 0 set is_target_unmodified_since_install 0
set target_cksum_compare "unknown" set target_cksum_compare "unknown"
set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list
if {[dict exists $latest_install_record -targets_cksums]} { if {[dict exists $latest_install_record -targets_cksums]} {
set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder) set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder)
if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} { if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} {
set is_target_unmodified_since_install 1 set is_target_unmodified_since_install 1
set target_cksum_compare "match" set target_cksum_compare "match"
@ -1797,6 +1829,7 @@ namespace eval punkcheck {
set ts_now [clock microseconds] set ts_now [clock microseconds]
set elapsed_us [expr {$ts_now - $ts_start}] set elapsed_us [expr {$ts_now - $ts_start}]
@ -1827,6 +1860,29 @@ namespace eval punkcheck {
lset punkcheck_records $existing_filerec_posn $filerec lset punkcheck_records $existing_filerec_posn $filerec
} }
#------------------------------------------------------------
if {$is_skip} {
set mark .
} else {
if {$is_new} {
set mark \x1b\[32\;1mN\x1b\[m
} else {
#updated
set mark \x1b\[32\;1mU\x1b\[m
}
}
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH]$mark
flush $opt_progresschannel
set last_depth $CALLDEPTH
} else {
puts -nonewline $opt_progresschannel $mark
}
}
#------------------------------------------------------------
} }
if {$max_depth != -1 && $CALLDEPTH >= $max_depth} { if {$max_depth != -1 && $CALLDEPTH >= $max_depth} {
@ -1905,6 +1961,7 @@ namespace eval punkcheck {
-punkcheck_folder $punkcheck_folder\ -punkcheck_folder $punkcheck_folder\
-punkcheck_eventid $punkcheck_eventid\ -punkcheck_eventid $punkcheck_eventid\
-punkcheck_records $punkcheck_records\ -punkcheck_records $punkcheck_records\
-progresschannel $opt_progresschannel\
] ]
set sub_opts [dict merge $opts $sub_opts] set sub_opts [dict merge $opts $sub_opts]
set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts] set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts]

1
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm

@ -60,6 +60,7 @@ package require punk::ansi
package require punk::lib package require punk::lib
catch {package require patternpunk} catch {package require patternpunk}
package require overtype package require overtype
package require struct::set
#safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list. #safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list.
#2025 - required term::ansi features for altg now built in to textblock #2025 - required term::ansi features for altg now built in to textblock

34
src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl

@ -1653,7 +1653,7 @@ if {$::punkboot::command eq "bootsupport"} {
file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists file mkdir [file dirname $tgtfile] ;#ensure containing folder for target exists
$boot_event targetset_started $boot_event targetset_started
# -- --- --- --- --- --- # -- --- --- --- --- ---
puts "BOOTSUPPORT module$which update: $srcfile -> $tgtfile" puts "\nBOOTSUPPORT module$which update: $srcfile -> $tgtfile"
if {[catch { if {[catch {
file copy -force $srcfile $tgtfile file copy -force $srcfile $tgtfile
} errM]} { } errM]} {
@ -1719,7 +1719,8 @@ if {$::punkboot::command eq "bootsupport"} {
set resultdict [punkcheck::install $sourcemodules $targetroot\ set resultdict [punkcheck::install $sourcemodules $targetroot\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\ -antiglob_paths $antipaths\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
@ -1737,7 +1738,8 @@ if {$::punkboot::command eq "bootsupport"} {
-max_depth 1\ -max_depth 1\
-createempty 0\ -createempty 0\
-overwrite installedsourcechanged-targets\ -overwrite installedsourcechanged-targets\
-installer "punkboot-bootsupport" -installer "punkboot-bootsupport"\
-progresschannel stdout\
] ]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
flush stdout flush stdout
@ -1784,7 +1786,12 @@ if {$::punkboot::command in {project modules}} {
#install .tm *and other files* #install .tm *and other files*
puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)" puts stdout "VENDORMODULES$which: copying from $sourcefolder/$vf to $target_module_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder -installer make.tcl -overwrite installedsourcechanged-targets -antiglob_paths {README.md include_modules.config}] set resultdict [punkcheck::install $sourcefolder/$vf $target_module_folder\
-installer make.tcl\
-overwrite installedsourcechanged-targets\
-antiglob_paths {README.md include_modules.config}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendormodulefolders]} { if {![llength $vendormodulefolders]} {
@ -1810,7 +1817,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "VENDORLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $vendorlibfolders]} { if {![llength $vendorlibfolders]} {
@ -1906,7 +1917,11 @@ if {$::punkboot::command in {project modules}} {
README.md\ README.md\
] ]
puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)" puts stdout "PROJECTLIB$which: copying from $sourcefolder/$lf to $target_lib_folder (if source file changed)"
set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder -overwrite installedsourcechanged-targets -antiglob_paths $antipaths] set resultdict [punkcheck::install $sourcefolder/$lf $target_lib_folder\
-overwrite installedsourcechanged-targets\
-antiglob_paths $antipaths\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }
if {![llength $projectlibfolders]} { if {![llength $projectlibfolders]} {
@ -1941,7 +1956,12 @@ if {$::punkboot::command in {project modules}} {
set overwrite "installedsourcechanged-targets" set overwrite "installedsourcechanged-targets"
#set overwrite "ALL-TARGETS" #set overwrite "ALL-TARGETS"
puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)" puts stdout "MODULEFOLDER non_tm_files $src_module_dir - copying to $target_modules_base (if source file changed)"
set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base -installer make.tcl -overwrite $overwrite -antiglob_paths {README.md}] set resultdict [punkcheck::install_non_tm_files $src_module_dir $target_modules_base\
-installer make.tcl\
-overwrite $overwrite\
-antiglob_paths {README.md}\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict] puts stdout [punkcheck::summarize_install_resultdict $resultdict]
} }

6
src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm

@ -4468,13 +4468,13 @@ tcl::namespace::eval punk::args {
#error "punk::args::parse Even number of -flag val pairs required after arglist" #error "punk::args::parse Even number of -flag val pairs required after arglist"
#} #}
#Default the -errorstyle to enhanced #Default the -errorstyle to standard
# (slowest on unhappy path - but probably clearest for playing with new APIs interactively) # (slow on unhappy path - but probably clearest for playing with new APIs interactively)
# - application devs should distribute a config file with an errorstyle override if desired. # - application devs should distribute a config file with an errorstyle override if desired.
# - devs who prefer a different default for interactive use should create a config for it. (todo) # - devs who prefer a different default for interactive use should create a config for it. (todo)
set defaultopts [dict create\ set defaultopts [dict create\
-form {*}\ -form {*}\
-errorstyle enhanced\ -errorstyle standard\
] ]
#todo - load override_errorstyle from configuration #todo - load override_errorstyle from configuration

125
src/vfs/_vfscommon.vfs/modules/punkcheck-0.1.0.tm

@ -32,9 +32,16 @@ package require punk::mix::util
#see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113 #see following article regarding the many problems with using mtime for build-decisions: https://apenwarr.ca/log/20181113
# #
namespace eval punkcheck { namespace eval punkcheck {
#namespace export\
# uuid\
# start_installer_event installfile_*
namespace export\ namespace export\
uuid\ uuid\
start_installer_event installfile_* installtrack\
install\
install_tm_files\
install_non_tm_files\
summarize_install_resultdict\
#antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators #antiglob_dir & antiglob_file entries match the pattern at any level - should not contain path separators
variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"] variable default_antiglob_dir_core [list "#*" "_aside" "_build" ".git" ".fossil*"]
@ -48,7 +55,7 @@ namespace eval punkcheck {
} }
if {!$has_twapi} { if {!$has_twapi} {
if {[catch {package require uuid} errM]} { if {[catch {package require uuid} errM]} {
error "Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows" error "punkcheck: Unable to load a package for uuid on this platform. Try tcllib's uuid (any platform) - twapi for windows"
} }
return [uuid::uuid generate] return [uuid::uuid generate]
} else { } else {
@ -150,6 +157,8 @@ namespace eval punkcheck {
} }
} }
#instances created by an installtrack object in method start_event
#also in installtrack constructor - to represent existing events from the .punkcheck data
oo::class create installevent { oo::class create installevent {
variable o_id variable o_id
variable o_rel_sourceroot variable o_rel_sourceroot
@ -266,7 +275,7 @@ namespace eval punkcheck {
set o_operation [string toupper $operation] set o_operation [string toupper $operation]
if {$o_operation_start_ts ne ""} { if {$o_operation_start_ts ne ""} {
error "[self] targetset_tart $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish." error "[self] targetset_init $o_operation operation already in progress. Use targetset_finished or targetset_complete to finish."
} }
set o_operation_start_ts [clock microseconds] set o_operation_start_ts [clock microseconds]
set seconds [expr {$o_operation_start_ts / 1000000}] set seconds [expr {$o_operation_start_ts / 1000000}]
@ -1241,7 +1250,17 @@ namespace eval punkcheck {
lappend PUNKARGS [list { lappend PUNKARGS [list {
@id -id ::punkcheck::install @id -id ::punkcheck::install
@cmd -name ::punkcheck::install -help\ @cmd -name ::punkcheck::install -help\
"Unidirectional file transfer to possibly non-empty target folder." "Unidirectional file transfer to possibly non-empty target folder.
This is the simpler form of the API, performing a transfer from one
directory tree to another, copying each file when changes in the source
file are detected.
Changes are detected by content checksum. The first install will record
source checksums in a .punkcheck file (ideally located at the root of the
target folder). Subsequent installs will compare stored checksums with
the current checksums of the source files.
For more advanced install operations, the object command installtrack
can be used to define install operations. e.g when the transfer is not
one-to-one and a target file depends on multiple source files."
@leaders -min 2 -max 2 @leaders -min 2 -max 2
srcdir -type directory srcdir -type directory
tgtdir -type directory tgtdir -type directory
@ -1251,7 +1270,7 @@ namespace eval punkcheck {
"Deepest subdirectory - use -1 for no limit." "Deepest subdirectory - use -1 for no limit."
-createdir -type boolean -default 0 -help\ -createdir -type boolean -default 0 -help\
"Whether to create the folder at tgtdir. "Whether to create the folder at tgtdir.
Any required subdirectories are created regardless of this setting." Any required subdirectories are created regardless of this setting."
-createempty -type boolean -default 0 -help\ -createempty -type boolean -default 0 -help\
"Whether to create folders at target that had no matches for our glob" "Whether to create folders at target that had no matches for our glob"
-glob -type string -default "*" -help\ -glob -type string -default "*" -help\
@ -1282,8 +1301,8 @@ namespace eval punkcheck {
} }
-punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\ -punkcheck_folder -default target -choices {target source project} -choicerestricted 0 -help\
"The location of the .punkcheck file to track installations and checksums. "The location of the .punkcheck file to track installations and checksums.
The default value 'target' is generally recommended. The default value 'target' is generally recommended.
Can also be an absolute path to a folder." Can also be an absolute path to a folder."
-punkcheck_records -default "" -help\ -punkcheck_records -default "" -help\
"Empty string or a parsed TDL records structure. "Empty string or a parsed TDL records structure.
e.g e.g
@ -1294,7 +1313,14 @@ namespace eval punkcheck {
}" }"
-installer -default "punkcheck::install" -help\ -installer -default "punkcheck::install" -help\
"A user nominated string that is stored in the .punkcheck file "A user nominated string that is stored in the .punkcheck file
This might be the name of a script or installation process." This might be the name of a script or installation process."
-progresschannel -default none -type string -help\
"Name of channel e.g stderr, stdout to which progress messages are written.
This includes the tree-like output consisting of dots (or green U) for each
file processed. As the number of files in a tree is not known beforehand,
it isn't useful for a percentage-based progress meter, but it could potentially
be used to drive a spinner if the textual data is not desired.
Setting to none or an invalid channel will deactivate the output."
}] }]
## unidirectional file transfer to possibly non empty folder ## unidirectional file transfer to possibly non empty folder
#default of -overwrite no-targets will only copy files that are missing at the target #default of -overwrite no-targets will only copy files that are missing at the target
@ -1343,6 +1369,7 @@ namespace eval punkcheck {
-punkcheck_eventid "\uFFFF"\ -punkcheck_eventid "\uFFFF"\
-punkcheck_records ""\ -punkcheck_records ""\
-installer punkcheck::install\ -installer punkcheck::install\
-progresschannel none\
] ]
if {([llength $args] %2) != 0} { if {([llength $args] %2) != 0} {
@ -1367,6 +1394,10 @@ namespace eval punkcheck {
set fileglob [dict get $opts -glob] set fileglob [dict get $opts -glob]
set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting set createdir [dict get $opts -createdir] ;#defaults to zero to help avoid mistakes with initial target dir - required target subdirs are created regardless of this setting
set opt_createempty [dict get $opts -createempty] set opt_createempty [dict get $opts -createempty]
set opt_progresschannel [dict get $opts -progresschannel]
if {$opt_progresschannel in {"" none} || [catch {chan configure $opt_progresschannel}]} {
set opt_progresschannel ""
}
if {$CALLDEPTH == 0} { if {$CALLDEPTH == 0} {
#expensive to normalize but we need to do it at least once #expensive to normalize but we need to do it at least once
@ -1485,6 +1516,7 @@ namespace eval punkcheck {
dict unset config -call-depth-internal dict unset config -call-depth-internal
dict unset config -max_depth dict unset config -max_depth
dict unset config -subdirlist dict unset config -subdirlist
dict unset config -progresschannel
tcl::dict::for {k v} $config { tcl::dict::for {k v} $config {
if {$v eq "\uFFFF"} { if {$v eq "\uFFFF"} {
dict unset config $k dict unset config $k
@ -1602,7 +1634,7 @@ namespace eval punkcheck {
#puts stdout "Current target dir: $current_target_dir" #puts stdout "Current target dir: $current_target_dir"
set last_processed_dir "" set last_depth ""
foreach m $match_list { foreach m $match_list {
set new_tgt_cksum_info [list] set new_tgt_cksum_info [list]
set relative_target_path [file join $relative_target_dir $m] set relative_target_path [file join $relative_target_dir $m]
@ -1620,14 +1652,7 @@ namespace eval punkcheck {
if {$is_antipath} { if {$is_antipath} {
continue continue
} }
#puts stdout " checking file : $current_source_dir/$m"
set thismatchdir [file dirname $m]
if {$last_processed_dir ne $thismatchdir} {
puts stdout "\n checking files in [file join $current_source_dir $thismatchdir]"
set last_processed_dir $thismatchdir
} else {
puts -nonewline stdout .
}
set ts_start [clock microseconds] set ts_start [clock microseconds]
set seconds [expr {$ts_start / 1000000}] set seconds [expr {$ts_start / 1000000}]
set ts_start_iso [clock format $seconds -format "%Y-%m-%dT%H:%M:%S"] set ts_start_iso [clock format $seconds -format "%Y-%m-%dT%H:%M:%S"]
@ -1639,7 +1664,9 @@ namespace eval punkcheck {
#change to use extract_or_create_fileset_record ? #change to use extract_or_create_fileset_record ?
set existing_filerec_posn [dict get $fetch_filerec_result position] set existing_filerec_posn [dict get $fetch_filerec_result position]
if {$existing_filerec_posn == -1} { if {$existing_filerec_posn == -1} {
puts stdout "NO existing record for $punkcheck_target_relpath" if {$opt_progresschannel ne ""} {
puts stdout "\nNO existing record for $punkcheck_target_relpath"
}
set has_filerec 0 set has_filerec 0
set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath] set new_filerec [dict create tag FILEINFO -targets $punkcheck_target_relpath]
set filerec $new_filerec set filerec $new_filerec
@ -1657,14 +1684,24 @@ namespace eval punkcheck {
unset new_install_record unset new_install_record
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
if {$CALLDEPTH <=1} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH][file tail $relative_source_dir]
}
flush $opt_progresschannel
##set last_depth $CALLDEPTH ;# done down below
}
}
set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m] set relative_source_path [lib::path_relative $punkcheck_folder $current_source_dir/$m]
#puts stdout " rel_source: $relative_source_path" #puts stdout " rel_source: $relative_source_path"
if {[file pathtype $relative_source_path] ne "relative"} { #if {[file pathtype $relative_source_path] ne "relative"} {
#REVIEW
#different volume or root #different volume or root
} #}
#Note this isn't a recordlist function - so it doesn't purely operate on the records #Note this isn't a recordlist function - so it doesn't purely operate on the records
#this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config. #this hits the filesystem for the sourcepath - gets checksums/timestamps depending on config.
#It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't) #It doesn't save to .punkcheck (the only punkcheck::installfile_ method which doesn't)
@ -1673,18 +1710,19 @@ namespace eval punkcheck {
set ts2 [clock milliseconds] set ts2 [clock milliseconds]
set diff [expr {$ts2 - $ts1}] set diff [expr {$ts2 - $ts1}]
if {$diff > 100} { if {$diff > 100} {
#todo -errorchannel
set errprefix ">>> punkcheck:" set errprefix ">>> punkcheck:"
puts stderr "$errprefix performance warning: fetch_metadata for $m took $diff ms." puts stderr "\n$errprefix performance warning: fetch_metadata for $m took $diff ms."
set lb [lindex [dict get $filerec body] end] set lb [lindex [dict get $filerec body] end]
#puts stderr "$errprefix filerec last body record:$lb" #puts stderr "$errprefix filerec last body record:$lb"
set records [dict get $lb body] set records [dict get $lb body]
set lr [lindex $records end] set lr [lindex $records end]
set alg [dict get $lr -cksum_all_opts -cksum_algorithm] set alg [dict get $lr -cksum_all_opts -cksum_algorithm]
if {$alg eq "sha1"} { if {$alg eq "sha1"} {
puts "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])" puts stderr "$errprefix cksum_algorithm: sha1 (accelerators: [::sha1::Implementations])"
puts "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]" puts stderr "$errprefix sha1 from: [package ifneeded sha1 [package present sha1]]"
} else { } else {
puts "$errprefix cksum_algorithm: $alg" puts stderr "$errprefix cksum_algorithm: $alg"
} }
} }
@ -1700,6 +1738,7 @@ namespace eval punkcheck {
} }
set is_skip 0 set is_skip 0
set is_new 0
if {$overwrite_what eq "all-targets"} { if {$overwrite_what eq "all-targets"} {
file mkdir $current_target_dir file mkdir $current_target_dir
#-------------------------------------------- #--------------------------------------------
@ -1710,12 +1749,13 @@ namespace eval punkcheck {
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
} else { } else {
if {![file exists $current_target_dir/$m]} { if {![file exists $current_target_dir/$m]} {
puts stderr "punkcheck: first copy to $current_target_dir/$m " #puts stderr "punkcheck: first copy to $current_target_dir/$m "
file mkdir $current_target_dir file mkdir $current_target_dir
file copy $current_source_dir/$m $current_target_dir file copy $current_source_dir/$m $current_target_dir
set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set new_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
lappend files_copied $current_source_dir/$m lappend files_copied $current_source_dir/$m
incr filecount_new incr filecount_new
set is_new 1
} else { } else {
switch -- $overwrite_what { switch -- $overwrite_what {
installedsourcechanged-targets { installedsourcechanged-targets {
@ -1737,14 +1777,16 @@ namespace eval punkcheck {
} }
} }
synced-targets { synced-targets {
#disallow overwriting of target that has been modified by some other mechanism
#review
if {[llength $changed]} { if {[llength $changed]} {
#only overwrite if the target checksum equals the last installed checksum (ie target is in sync with source and so hasn't been customized) #only overwrite if the target file checksum equals the last installed checksum (ie target is in sync with previous source-set and so hasn't been customized)
set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m] set existing_tgt_cksum_info [punk::mix::base::lib::cksum_path $current_target_dir/$m]
set is_target_unmodified_since_install 0 set is_target_unmodified_since_install 0
set target_cksum_compare "unknown" set target_cksum_compare "unknown"
set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list set latest_install_record [punkcheck::recordlist::file_record_latest_installrecord $filerec] ;#may be no such record - in which case we get an empty list
if {[dict exists $latest_install_record -targets_cksums]} { if {[dict exists $latest_install_record -targets_cksums]} {
set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder) set last_install_cksum [dict get $latest_install_record -targets_cksums] ;#in this case we know there is only one as 'install' always uses targetset size of 1. (FILEINFO record per file in source folder)
if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} { if {[dict get $existing_tgt_cksum_info cksum] eq $last_install_cksum} {
set is_target_unmodified_since_install 1 set is_target_unmodified_since_install 1
set target_cksum_compare "match" set target_cksum_compare "match"
@ -1787,6 +1829,7 @@ namespace eval punkcheck {
set ts_now [clock microseconds] set ts_now [clock microseconds]
set elapsed_us [expr {$ts_now - $ts_start}] set elapsed_us [expr {$ts_now - $ts_start}]
@ -1817,10 +1860,35 @@ namespace eval punkcheck {
lset punkcheck_records $existing_filerec_posn $filerec lset punkcheck_records $existing_filerec_posn $filerec
} }
#------------------------------------------------------------
if {$is_skip} {
set mark .
} else {
if {$is_new} {
set mark \x1b\[32\;1mN\x1b\[m
} else {
#updated
set mark \x1b\[32\;1mU\x1b\[m
}
}
if {$opt_progresschannel ne ""} {
if {$last_depth ne $CALLDEPTH} {
puts -nonewline $opt_progresschannel \n[string repeat " " $CALLDEPTH]$mark
flush $opt_progresschannel
set last_depth $CALLDEPTH
} else {
puts -nonewline $opt_progresschannel $mark
}
}
#------------------------------------------------------------
} }
if {$max_depth != -1 && $CALLDEPTH >= $max_depth} { if {$max_depth != -1 && $CALLDEPTH >= $max_depth} {
#don't process any more subdirs #don't process any more subdirs
#sometimes deliberately called with max_depth 1 - so don't warn here. review
#puts stderr "punkcheck::install warning - reached max_depth $max_depth"
set subdirs [list] set subdirs [list]
} else { } else {
set subdirs [glob -nocomplain -dir $current_source_dir -type d -tail *] set subdirs [glob -nocomplain -dir $current_source_dir -type d -tail *]
@ -1893,6 +1961,7 @@ namespace eval punkcheck {
-punkcheck_folder $punkcheck_folder\ -punkcheck_folder $punkcheck_folder\
-punkcheck_eventid $punkcheck_eventid\ -punkcheck_eventid $punkcheck_eventid\
-punkcheck_records $punkcheck_records\ -punkcheck_records $punkcheck_records\
-progresschannel $opt_progresschannel\
] ]
set sub_opts [dict merge $opts $sub_opts] set sub_opts [dict merge $opts $sub_opts]
set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts] set sub_result [punkcheck::install $srcdir $tgtdir {*}$sub_opts]
@ -1910,7 +1979,7 @@ namespace eval punkcheck {
if {[llength $files_copied] || [llength $files_skipped]} { if {[llength $files_copied] || [llength $files_skipped]} {
#puts stdout ">>>>>>>>>>>>>>>>>>>" #puts stdout ">>>>>>>>>>>>>>>>>>>"
set saveresult [punkcheck::save_records_to_file $punkcheck_records $punkcheck_file] set saveresult [punkcheck::save_records_to_file $punkcheck_records $punkcheck_file]
puts stdout "punkcheck::install [dict get $saveresult recordcount] records saved as [dict get $saveresult linecount] lines to $punkcheck_file copied: [llength $files_copied] skipped: [llength $files_skipped]" puts stdout "\npunkcheck::install [dict get $saveresult recordcount] records saved as [dict get $saveresult linecount] lines to $punkcheck_file copied: [llength $files_copied] skipped: [llength $files_skipped]"
#puts stdout ">>>>>>>>>>>>>>>>>>>" #puts stdout ">>>>>>>>>>>>>>>>>>>"
} else { } else {
#todo - write db INSTALLER record if -debug true #todo - write db INSTALLER record if -debug true

1
src/vfs/_vfscommon.vfs/modules/textblock-0.1.3.tm

@ -60,6 +60,7 @@ package require punk::ansi
package require punk::lib package require punk::lib
catch {package require patternpunk} catch {package require patternpunk}
package require overtype package require overtype
package require struct::set
#safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list. #safebase interps as at 2024-08 can't access deeper paths - even though they are below the supposed safe list.
#2025 - required term::ansi features for altg now built in to textblock #2025 - required term::ansi features for altg now built in to textblock

Loading…
Cancel
Save