#main.tcl - we expect to be in the context of a zipkit or tclkit vfs attached to a tcl executable. # or cookfs ? #review - what happens if multiple are somehow attached and for example both vfs and zipfs are available? # - if that's even possible - we have no control here over which main.tcl was selected as we're already here # a metakit data portion seems to need to be add the end of the file (from looking at sdx.kit code) # - todo - investigate if zipfs can be inserted between starkit head executable and metakit tail data #The logic below will add appropriate package paths from starkit and zipfs vfs paths # - and restrict package paths to those coming from a vfs (if not launched with 'dev' first arg which allows external paths to remain) apply { args { set tclmajorv [lindex [split [info tclversion] .] 0] set has_zipfs [expr {[info commands tcl::zipfs::root] ne ""}] if {$has_zipfs} { set has_zipfs_attached [expr {[llength [tcl::zipfs::mount]]}] } else { set has_zipfs_attached 0 } #REVIEW - cookit/cookfs can be compiled with a different name for it's mount-point # - we could examine the -handle from 'file attr' for each //something:/ volume (excluding //zipfs:/) # - but there are situations where handle is empty (? punk repl issue?) # - for now we only support the known name - REVIEW set has_cookfs [expr {"//cookit:/" in [file volumes]}] set cookbase //cookit:/ ;#always define it so we can test on it later.. if {$has_cookfs} { set has_cookfs_attached [file exists //cookit:/lib] ;# //cookit:/manifest.txt ? REVIEW } else { set has_cookfs_attached 0 } #here we make an attempt to avoid premature (costly) auto_path/tcl::tm::list scanning caused by our initial 'package require starkit'. #we will first look for a starkit.tcl in an expected location and try to load that, then fallback to package require. #standard way to avoid symlinking issues - review! set normscript [file dirname [file normalize [file join [info script] __dummy__]]] set normexe [file dirname [file normalize [file join [info nameofexecutable] __dummy__]]] set topdir [file dirname $normscript] set found_starkit_tcl 0 set possible_lib_vfs_folders [glob -nocomplain -dir [file join $topdir lib] -type d vfs*] foreach test_folder $possible_lib_vfs_folders { #e.g /lib/vfs1.4.1 #we don't expect multiple vfs* folders - but we will process any found and load the pkgIndex.tcl from these folders. #order of folder processing shouldn't matter (rely on order returned by 'package versions' - review) if {[file exists $test_folder/starkit.tcl] && [file exists $test_folder/pkgIndex.tcl]} { set dir $test_folder source $test_folder/pkgIndex.tcl } } if {[set starkitv [lindex [package versions starkit] end]] ne ""} { #run the ifneeded script for the latest found (assuming package versions ordering is correct) eval [package ifneeded starkit $starkitv] set found_starkit_tcl 1 } if {!$found_starkit_tcl} { #our internal 'quick' search for starkit failed. #either we are in a pure zipfs system, or cookfs - or the starkit package is somewhere more devious #for pure zipfs or cookfs - it's a little wasteful to perform exhaustive search for starkit #review - only keep searching if not 'dev' first arg? #Initially we've done no scans of auto_path/tcl::tm::list - but there will already be a core set of packages known by the kit #retain it so we can 'forget' the difference after our first 'package require' forces a full scan which includes some paths we may not wish to include or at least include with different preferences #puts "main.tcl 1)--> package name count: [llength [package names]]" #puts stderr [join [package names] \n] set original_packages [package names] if {![catch {package require starkit}]} { #known side-effects of starkit::startup #sets the ::starkit::mode variable to the way in which it was launched. One of: {starpack starkit unwrapped tclhttpd plugin service sourced} #set the ::starkit::topdir variable #if mode not starpack, then: # - adds $::starkit::topdir/lib to the auto_path if not already present # #In this context (vfs attached to tcl kit executable - we expect the launch mode to be 'starkit' set starkit_startmode [starkit::startup] puts stderr "STARKIT MODE: $starkit_startmode" } #puts "main.tcl 2)--> package name count: [llength [package names]]" foreach pkg [package names] { if {$pkg ni $original_packages} { package forget $pkg } } #puts "main.tcl 3)--> package name count: [llength [package names]]" } # -- --- --- #when run as a tclkit - the exe is mounted as a dir and Tcl's auto_execok doesn't find it. review - for what versions of Tcl does this apply? #known to occur in old 8.6.8 kits as well as 8.7 #review - do we want $normexe or [info nameofexecutable] for $thisexe here? Presumably [info nameofexecutable] (possible symlink) ok #we want to be able to launch a process from the interactive shell using the same name this one was launched with. set thisexe [file tail [info nameofexecutable]] ;#e.g punk86.exe set thisexeroot [file rootname $thisexe] ;#e.g punk86 set ::auto_execs($thisexeroot) [info nameofexecutable] if {$thisexe ne $thisexeroot} { #on windows make the .exe point there too set ::auto_execs($thisexe) [info nameofexecutable] } # -- --- --- set tm_additions_internal [list] set tm_additions_dev [list] set auto_path_additions_internal [list] set auto_path_additions_dev [list] set lc_auto_path [string tolower $::auto_path] #inital auto_path setup by init.tcl #firstly it includes env(TCLLIBPATH) #then it adds the tcl_library folder and its parent #e.g //zipfs:/app/tcl_library and //zipfs:/app #when 'dev' or 'os' is not supplied - any non internal paths (usually those from env(TCLLIBPATH) will be stripped #so that everything is self-contained in the kit/zipkit #puts "\x1b\[1\;33m main.tcl original auto_path: $::auto_path" if {[info exists ::tcl::kitpath] && $::tcl::kitpath ne ""} { set kp $::tcl::kitpath #set existing_module_paths [string tolower [tcl::tm::list]] foreach p [list modules modules_tcl$tclmajorv] { #if {[string tolower [file join $kp $p]] ni $existing_module_paths} { # tcl::tm::add [file join $kp $p] #} lappend tm_additions_internal [file join $kp $p] } foreach p [list lib lib_tcl$tclmajorv] { lappend auto_path_additions_internal [file join $kp $p] } } if {$has_zipfs_attached} { #review build option may be different - tclZipFs.c ZIPFS_APP_MOUNT defaults to ZIPFS_VOLUME/app - but it could be something else. (why?) #default 'zipfs root' has trailing slash (//zipfs:/) - but file join does the right thing set zipbase [file join [tcl::zipfs::root] app] if {"$zipbase" in [tcl::zipfs::mount]} { #set existing_module_paths [string tolower [tcl::tm::list]] foreach p [list modules modules_tcl$tclmajorv] { #if {[string tolower [file join $zipbase $p]] ni $existing_module_paths} { # tcl::tm::add [file join $zipbase $p] #} lappend tm_additions_internal [file join $zipbase $p] } foreach p [list lib lib_tcl$tclmajorv] { lappend auto_path_additions_internal [file join $zipbase $p] } } } if {$has_cookfs_attached} { #set existing_module_paths [string tolower [tcl::tm::list]] foreach p [list modules modules_tcl$tclmajorv] { #if {[string tolower [file join $cookbase $p]] ni $existing_module_paths} { # tcl::tm::add [file join $cookbase $p] #} lappend tm_additions_internal [file join $cookbase $p] } foreach p [list lib lib_tcl$tclmajorv] { lappend auto_path_additions_internal [file join $cookbase $p] } } set internal_paths [list] if {$has_zipfs} { set ziproot [tcl::zipfs::root] ;#root is enough to determine internal zipkit path lappend internal_paths $ziproot } if {[info exists ::tcl::kitpath] && $::tcl::kitpath ne ""} { lappend internal_paths $::tcl::kitpath } if {$has_cookfs} { lappend internal_paths $cookbase } #REVIEW if {[info exists ::punkboot::internal_paths] && [llength $::punkboot::internal_paths]} { #somewhat ugly cooperation with external sourcing scripts lappend internal_paths {*}$::punkboot::internal_paths } # ----------------------------------------------------------------------------------------------------------- # dev - refers to module and library paths relative to the project (executable path) # os - refers to modules and library paths gleaned from ::env (TCLLIBPATH and TCL__TM_PATH) # internal - refers to modules and libraries supplied from the mounted filesystem of a kit or zipfs based executable # ----------------------------------------------------------------------------------------------------------- # Note that unlike standard 'package unknown' punk::libunknown does not stop searching for packages when a .tm file is found that matches requirements, # The auto_path is still examined. (avoids quirks where higher versioned pkgIndex based package not always found) # ----------------------------------------------------------------------------------------------------------- set all_package_modes [list dev os internal] #package_mode is specified as a dash-delimited ordered value e.g dev-os #"internal" is the default and if not present is always added to the list #i.e "dev-os" is equivalent to "dev-os-internal" #"os" is equivalent to "os-internal" #"internal-os" and "internal" are left as is. #The effective package_mode has 1 2 or 3 members. # The only case where it has 1 member is if just "internal" is specified. #This gives the number of permutations as how many ways to choose 3 items plus how many ways to choose 2 of the 3 items (one must be 'internal') plus the sole allowable way to choose 1 #for a total of 11 possible final orderings. #(16 possible values for package_mode argument when you include the short-forms "",os,dev,os-dev,dev-os which always have 'internal' appended) set test_package_mode [lindex $args 0] switch -exact -- $test_package_mode { internal - os-internal - dev-internal - internal-os - internal-dev - os-dev-internal - os-internal-dev - dev-os-internal - dev-internal-os - internal-os-dev - internal-dev-os { #fully specified ('internal' is present) set package_modes [split $test_package_mode -] set arglist [lrange $args 1 end] } os - dev - os-dev - dev-os { #partially specified - 'internal' ommitted but implied at tail set package_modes [list {*}[split $test_package_mode -] internal] set arglist [lrange $args 1 end] } default { #empty first arg - or some unrelated arg set package_modes internal if {$test_package_mode eq ""} { #consume the empty first arg as an equivalent of 'internal' #don't consume any first arg that isn't recognised as a package_mode set arglist [lrange $args 1 end] } else { set arglist $args } } } #assert: arglist has had any first arg that is a package_mode (including empty string) stripped. set ::argv $arglist set ::argc [llength $arglist] #assert: package_modes is now a list of at least length 1 (in which case the only possible value is: internal) #Note regarding the use of package forget and binary packages #If the package has loaded a binary component - then a package forget and a subsequent package require can result in both binaries being present, as seen in 'info loaded' result - potentially resulting in anomalous behaviour #In general package forget after a package has already been required may need special handling and should be avoided where possible. #Only a limited set of packages support unloading a binary component anyway. #We limit the use of 'package forget' here to packages that have not been loaded (whether pure-tcl or not) #ie in this context it is used only for manipulating preferences of which packages are loaded in the first place #Unintuitive preferencing can occur if the same package version is for example present in a tclkit and in a module or lib folder external to the kit. #It may be desired for performance or testing reasons to preference the library outside of the kit - and raising the version number may not always be possible/practical. #If the executable is a kit - we don't know what packages it contains or whether it allows loading from env based external paths. #For app-punk projects - the lib/module paths based on the project being run should take preference if 'dev' is earlier in the list, even if the version number is the same. #(these are the 'info nameofexecutable' or 'info script' or 'pwd' relative paths that are added here) #Some kits will remove lib/module paths (from auto_path & tcl::tm::list) that have been added via TCLLIBPATH / TCLX_Y_TM_PATH environment variables #Some kits will remove those env-provided lib paths but fail to remove the env-provided module paths #(differences in boot.tcl in the kits) if {[llength $package_modes] > 1} { puts stderr "main.tcl PACKAGE MODE is preferencing libraries and modules in the order: $package_modes" puts stderr "main.tcl original auto_path: $::auto_path" #------------------------------------------------------------------------------ #Module loading #------------------------------------------------------------------------------ #If the current directory contains .tm files when the punk repl starts - then it will attempt to preference them # - but first add our other known relative modules paths - as it won't make sense to use current directory as a modulepath if it's an ancestor of one of these.. #original tm list at this point consists of whatever the kit decided + some prepended internal kit paths that punk decided on. #we want to bring the existing external paths to the position specified by package_mode (probably from the kit looking at various env TCL* values) #we want to maintain the order of the internal paths. #we want to add our external dev paths to the position specified by package_mode #assert [llength [package names]] should be small at this point ~ <10 ? set original_tm_list [tcl::tm::list] tcl::tm::remove {*}$original_tm_list # -- --- --- --- --- --- --- --- #split existing paths into internal & external set internal_tm_dirs [list] ;# set external_tm_dirs [list] set lcase_internal_paths [string tolower $internal_paths] foreach tm $original_tm_list { set tmlower [string tolower $tm] set is_internal 0 foreach okprefix $lcase_internal_paths { if {[string match "$okprefix*" $tmlower]} { lappend internal_tm_dirs $tm set is_internal 1 break } } if {!$is_internal} { lappend external_tm_dirs $tm } } # -- --- --- --- --- --- --- --- set original_external_tm_dirs $external_tm_dirs ;#we check some of our additions and bring to front - so we refer to external list as provided by kit #assert internal_tm_dirs and external_tm_dirs have their case preserved.. set module_folders [list] #review - the below statement doesn't seem to be true. #tm list first added end up later in the list - and then override earlier ones if version the same - so add pwd-relative 1st to give higher priority #(only if Tcl has scanned all paths - see below bogus package load) #1 #2) # .../bin/punkXX.exe look for ../modules (i.e modules folder at same level as bin folder) #using normexe under assumption [info name] might be symlink - and more likely to be where the modules are located. #we will try both relative to symlink and relative to underlying exe - with those at symlink location earlier in the list #review - a user may have other expectations. #case differences could represent different paths on unix-like platforms. #It's perhaps a little unwise to configure matching paths with only case differences for a cross-platform tool .. but we should support it for those who use it and have no interest in windows - todo! review if {"dev" in $package_modes} { set normexe_dir [file dirname $normexe] if {[file tail $normexe_dir] eq "bin"} { #underlying exe in a bin dir - backtrack 1 lappend exe_module_folders [file dirname $normexe_dir]/modules lappend exe_module_folders [file dirname $normexe_dir]/modules_tcl$tclmajorv } else { lappend exe_module_folders $normexe_dir/modules lappend exe_module_folders $normexe_dir/modules_tcl$tclmajorv } set nameexe_dir [file dirname [info nameofexecutable]] #possible symlink (may resolve to same path as above - we check below to not add in twice) if {[file tail $nameexe_dir] eq "bin"} { lappend exe_module_folders [file dirname $nameexe_dir]/modules lappend exe_module_folders [file dirname $nameexe_dir]/modules_tcl$tclmajorv } else { lappend exe_module_folders $nameexe_dir/modules lappend exe_module_folders $nameexe_dir/modules_tcl$tclmajorv } #foreach modulefolder $exe_module_folders { # set lc_external_tm_dirs [string tolower $external_tm_dirs] # set lc_modulefolder [string tolower $modulefolder] # if {$lc_modulefolder in [string tolower $original_external_tm_dirs]} { # #perhaps we have an env var set pointing to one of our dev foldersl. We don't want to rely on how the kit ordered it. # #bring to front if not already there. # #assert it must be present in $lc_external_tm_dirs if it's in $original_external_tm_dirs # set posn [lsearch $lc_external_tm_dirs $lc_modulefolder] # if {$posn > 0} { # #don't rely on lremove here. Not all runtimes have it and we don't want to load our forward-compatibility packages yet. # #(still need to support tcl 8.6 - and this script used in multiple kits) # set external_tm_dirs [lreplace $external_tm_dirs $posn $posn] # #don't even add it back in if it doesn't exist in filesystem # if {[file isdirectory $modulefolder]} { # set external_tm_dirs [linsert $external_tm_dirs 0 $modulefolder] # } # } # } else { # if {$lc_modulefolder ni $lc_external_tm_dirs && [file isdirectory $modulefolder]} { # set external_tm_dirs [linsert $external_tm_dirs 0 $modulefolder] ;#linsert seems faster than 'concat [list $modulefolder] $external_tm_dirs' - review # } # } #} if {![llength $exe_module_folders]} { puts stderr "Warning - no 'modules' or 'modules_tcl$tclmajorv' folders found relative to executable (or it's symlink if any)" } else { set tm_additions_dev $exe_module_folders } } if {"os" in $package_modes} { #2) support developer running from a folder containing *.tm files they want to make available # could cause problems if user happens to be in a subdirectory of a tm folder structure as namespaced modules won't work if not at a tm path root. #The current dir could also be a subdirectory of an existing tm_dir which would fail during tcl::tm::add - we will need to wrap all additions in catch set currentdir_modules [glob -nocomplain -dir [pwd] -type f -tail *.tm] #we assume [pwd] will always return an external (not kit) path at this point - REVIEW if {[llength $currentdir_modules]} { #now add current dir (if no conflict with above) set external_tm_dirs [linsert $external_tm_dirs 0 $currentdir_modules] if {[file exists [pwd]/modules] || [file exists [pwd]/modules_tcl$tclmajorv]} { puts stderr "WARNING: modules or modules_tcl$tclmajorv folders not added to tcl::tm::path due to modules found in current workding dir [pwd]" } } else { #modules or modules_tclX subdir relative to cwd cannot be added if [pwd] has been added set cwd_modules_folder [file normalize [file join [pwd] modules]] if {[file isdirectory $cwd_modules_folder]} { if {[string tolower $cwd_modules_folder] ni [string tolower $external_tm_dirs]} { #prepend set external_tm_dirs [linsert $external_tm_dirs 0 $cwd_modules_folder] } } set cwd_modules_folder [file normalize [file join [pwd] modules_tcl$tclmajorv]] if {[file isdirectory $cwd_modules_folder]} { if {[string tolower $cwd_modules_folder] ni [string tolower $external_tm_dirs]} { #prepend set external_tm_dirs [linsert $external_tm_dirs 0 $cwd_modules_folder] } } } } #assert tcl::tm::list still empty here #restore module paths # -- --- --- --- --- --- --- --- set new_tm_path [list] foreach mode $package_modes { switch -exact -- $mode { internal { #review #even though the internal_tm_dirs came from either ::env or the executable's init - we don't treat them as 'os' paths #Add them before our own internal additions foreach n $internal_tm_dirs { if {$n ni $new_tm_path} { lappend new_tm_path $n } } foreach n $tm_additions_internal { if {$n ni $new_tm_path} { lappend new_tm_path $n } } } dev { foreach n $tm_additions_dev { if {$n ni $new_tm_path} { lappend new_tm_path $n } } } os { foreach n $external_tm_dirs { if {$n ni $new_tm_path} { lappend new_tm_path $n } } } } } foreach p [lreverse $new_tm_path] { if {[catch {tcl::tm::add $p} errM]} { puts stderr "Failed to add tm module dir '$p' to tcl::tm::list\n$errM" } } ##tcl::tm::add internals first (so they end up at the end of the tmlist) as in 'dev' mode (dev as first argument on launch) we preference external modules ##note use of lreverse to maintain same order #foreach p [lreverse $internal_tm_dirs] { # if {$p ni [tcl::tm::list]} { # #Items that end up at the beginning of the tm list are processed first.. but an item of same version later in the tm list will not override the ifneeded script of an already encountered .tm. # #addition can fail if one path is a prefix of another # if {[catch {tcl::tm::add $p} errM]} { # puts stderr "Failed to add internal module dir '$p' to tcl::tm::list\n$errM" # } # } #} ##push externals to *head* of tcl::tm::list - as they have priority #foreach p [lreverse $external_tm_dirs] { # if {$p ni [tcl::tm::list]} { # if {[catch {tcl::tm::add $p} errM]} { # puts stderr "Failed to add external module dir '$p' to tcl::tm::list\n$errM" # } # } #} #AUTO_PATH #auto_path - add *external* exe-relative after exe-relative path #add lib and lib_tcl8 lib_tcl9 etc based on tclmajorv #libs appended to end of ::auto_path are processed first (reverse order processing in 'package unknown'), but ifneeded scripts are overridden by earlier ones #(ie for both tcl::tm::list and auto_path it is priority by 'order of appearance' in the resultant lists - not the order in which they are added to the lists) # #we can't rely on builtin ledit (tcl9+) or loadable version such as punk::lib::compat::ledit at this point #so we prepend to auto_path using a slightly inefficient method. Should be fine on relatively small list like this #eventually it should just be something like 'ledit ::auto_path -1 -1 $libfolder' if {"dev" in $package_modes} { if {"windows" eq $::tcl_platform(platform)} { #case differences dont matter - but can stop us finding path in auto_path foreach libsub [list lib_tcl$tclmajorv lib] { if {[file tail $nameexe_dir] eq "bin"} { set libfolder [file dirname $nameexe_dir]/$libsub } else { set libfolder $nameexe_dir/$libsub } if {[file isdirectory $libfolder]} { lappend auto_path_additions_dev $libfolder } # ------------- if {[file tail $normexe_dir] eq "bin"} { set libfolder [file dirname $normexe_dir]/$libsub } else { set libfolder $normexe_dir/$libsub } if {[file isdirectory $libfolder]} { lappend auto_path_additions_dev $libfolder } # ------------- set libfolder [pwd]/$libsub if {[file isdirectory $libfolder]} { lappend auto_path_additions_dev $libfolder } } } else { #on other platforms, case differences could represent different paths foreach libsub [list lib_tcl$tclmajorv lib] { if {[file tail $nameexe_dir] eq "bin"} { set libfolder [file dirname $nameexe_dir]/$libsub } else { set libfolder $nameexe_dir/$libsub } if {[file isdirectory $libfolder]} { lappend auto_path_additions_dev $libfolder } # ------------- if {[file tail $normexe_dir] eq "bin"} { set libfolder [file dirname $normexe_dir]/$libsub } else { set libfolder $normexe_dir/$libsub } if {[file isdirectory $libfolder]} { lappend auto_path_additions_dev $libfolder } # ------------- set libfolder [pwd]/$libsub if {[file isdirectory $libfolder]} { lappend auto_path_additions_dev $libfolder } } } } # -- --- --- --- --- --- --- --- #split existing ::auto_path entries into internal & external set internal_ap_dirs [list] ;# set external_ap_dirs [list] set lcase_internal_paths [string tolower $internal_paths] foreach pkgpath $::auto_path { set pkgpathlower [string tolower $pkgpath] set is_internal 0 foreach okprefix $lcase_internal_paths { if {[string match "$okprefix*" $pkgpathlower]} { lappend internal_ap_dirs $pkgpath set is_internal 1 break } } if {!$is_internal} { lappend external_ap_dirs $pkgpath } } # -- --- --- --- --- --- --- --- set new_auto_path [list] foreach mode $package_modes { switch -exact -- $mode { internal { #review #even though the internal_ap_dirs came from either ::env or the executable's init - we don't treat them as 'os' paths #Add them before our own internal additions foreach n $internal_ap_dirs { if {$n ni $new_auto_path} { lappend new_auto_path $n } } foreach n $auto_path_additions_internal { if {$n ni $new_auto_path} { lappend new_auto_path $n } } } dev { foreach n $auto_path_additions_dev { if {$n ni $new_auto_path} { lappend new_auto_path $n } } } os { foreach n $external_ap_dirs { if {$n ni $new_auto_path} { lappend new_auto_path $n } } } } } set ::auto_path $new_auto_path } else { #package_mode 'internal' only #Tcl_Init will most likely have set up some external paths #As our app has been started without first arg (package_mode) indicating anything other than 'internal' - we will prune paths that are not zipfs or tclkit #(or set via punkboot::internal_paths) set filtered_auto_path [list] #review - case insensitive ok for windows - but could cause issues on other platforms? foreach ap $::auto_path { set aplower [string tolower $ap] foreach okprefix $internal_paths { if {[string match "[string tolower $okprefix]*" $aplower]} { lappend filtered_auto_path $ap break } } } puts stderr "main.tcl internal_paths: $internal_paths" puts stderr "main.tcl filtered_auto_path: $filtered_auto_path" set filtered_tm_list [list] foreach tm [tcl::tm::list] { set tmlower [string tolower $tm] foreach okprefix $internal_paths { if {[string match "[string tolower $okprefix]*" $tmlower]} { lappend filtered_tm_list $tm break } } } set new_tm_list [list] foreach p $filtered_tm_list { if {$p ni $new_tm_list && [file exists $p]} { lappend new_tm_list $p } } foreach p $tm_additions_internal { if {$p ni $new_tm_list && [file exists $p]} { lappend new_tm_list $p } } tcl::tm::remove {*}[tcl::tm::list] tcl::tm::add {*}[lreverse $new_tm_list] #If it looks like we are running the vfs/_build/exename.vfs/main.tcl from an external tclsh - try to use vfs folders to simulate kit state #set script_relative_lib [file normalize [file join [file dirname [info script]] lib]] #set scriptdir [file dirname [info script]] set scriptdir [file dirname $normscript] if {![string match //zipfs:/* $scriptdir] && ![string match "${cookbase}*" $scriptdir] && ![info exists ::tcl::kitpath]} { #presumably running the vfs/xxx.vfs/main.tcl script using a non-kit tclsh that doesn't have starkit lib or mounted zipfs/cookfs available.. lets see if we can move forward anyway set vfscontainer [file normalize [file dirname $scriptdir]] #set vfscommon [file join $vfscontainer _vfscommon] #we shouldn't be targetting the src/vfs folders - use src/_build/exename.vfs instead set vfsdir [file normalize $scriptdir] set projectroot [file dirname [file dirname $vfscontainer]] ;#back below src/_build/exename.vfs/main.tcl puts stdout "no starkit. projectroot?: $projectroot executable:[info nameofexecutable]" puts stdout "info lib: [info library]" #add back the info lib reported by the executable.. as we can't access the one built into a kit if {[file exists [info library]]} { if {[string tolower [info library]] ni [string tolower [list {*}$filtered_auto_path {*}$auto_path_additions_internal]]} { lappend auto_path_additions_internal [info library] } } set lib_types [list lib lib_tcl$tclmajorv] foreach l $lib_types { set lib [file join $vfsdir $l] if {[file exists $lib] && [string tolower $lib] ni [string tolower [list {*}$filtered_auto_path {*}$auto_path_additions_internal]]} { lappend auto_path_additions_internal $lib } } #foreach l $lib_types { # set lib [file join $vfscommon $l] # if {[file exists $lib] && [string tolower $lib] ni [string tolower $::auto_path]} { # lappend ::auto_path $lib # } #} set ::auto_path [list {*}$filtered_auto_path {*}$auto_path_additions_internal] puts stderr "main.tcl final auto_path: $::auto_path" set mod_types [list modules modules_tcl$tclmajorv] foreach m $mod_types { set modpath [file join $vfsdir $m] if {[file exists $modpath] && [string tolower $modpath] ni [string tolower [tcl::tm::list]]} { tcl::tm::add $modpath } } #foreach m $mod_types { # set modpath [file join $vfscommon $m] # if {[file exists $modpath] && [string tolower $modpath] ni [string tolower [tcl::tm::list]]} { # tcl::tm::add $modpath # } #} } else { #normal case main.tcl from vfs set ::auto_path [list {*}$filtered_auto_path {*}$auto_path_additions_internal] } #force rescan #catch {package require flobrudder666_nonexistant} puts stderr "main.tcl auto_path :$::auto_path" puts stderr "main.tcl tcl::tm::list:[tcl::tm::list]" } if {$has_zipfs_attached} { #load libunknown without triggering the existing package unknown #maint: also in punk::repl package #-------------------------------------------------------- set libunks [list] foreach tm_path [tcl::tm::list] { set punkdir [file join $tm_path punk] if {![file exists $punkdir]} {continue} lappend libunks {*}[glob -nocomplain -dir $punkdir -type f libunknown-*.tm] } set libunknown "" set libunknown_version_sofar "" foreach lib $libunks { #expecting to be of form libunknown-.tm set vtail [lindex [split [file tail $lib] -] 1] set thisver [file rootname $vtail] ;#file rootname x.y.z.tm if {$libunknown_version_sofar eq ""} { set libunknown_version_sofar $thisver set libunknown $lib } else { if {[package vcompare $thisver $libunknown_version_sofar] == 1} { set libunknown_version_sofar $thisver set libunknown $lib } } } if {$libunknown ne ""} { source $libunknown if {[catch {punk::libunknown::init -caller main.tcl} errM]} { puts "error initialising punk::libunknown\n$errM" } } #-------------------------------------------------------- #set zr [::tcl::zipfs::root] ;#always ends with / ? - REVIEW #if {[file join $zr app modules] in [tcl::tm::list]} { # #todo - better way to find latest version - without package require # set lib [file join $zr app modules punk libunknown.tm] # if {[file exists $lib]} { # source $lib # punk::libunknown::init # #package unknown {punk::libunknown::zipfs_tm_UnknownHandler punk::libunknown::zipfs_tclPkgUnknown} # } #} } #assert arglist has had 'dev' first arg removed if it was present. if {[llength $arglist] == 1 && [lindex $arglist 0] eq "tclsh"} { #called as dev tclsh or tclsh #we would like to drop through to standard tclsh repl without launching another process #tclMain.c doesn't allow it unless patched. if {![info exists ::env(TCLSH_PIPEREPL)]} { set is_tclsh_piperepl_env_true 0 } else { if {[string is boolean -strict $::env(TCLSH_PIPEREPL)]} { set is_tclsh_piperepl_env_true $::env(TCLSH_PIPEREPL) } else { set is_tclsh_piperepl_env_true 0 } } if {!$is_tclsh_piperepl_env_true} { puts stderr "tcl_interactive: $::tcl_interactive" puts stderr "stdin: [chan configure stdin]" puts stderr "Environment variable TCLSH_PIPEREPL is not set or is false or is not a boolean" } else { #according to env TCLSH_PIPEREPL and our commandline argument - tclsh repl is desired #check if tclsh/punk has had the piperepl patch applied - in which case tclsh(istty) should exist if {![info exists ::tclsh(istty)]} { puts stderr "error: the runtime doesn't appear to have been compiled with the piperepl patch" } } set ::tcl_interactive 1 set ::tclsh(dorepl) 1 } elseif {[llength $arglist]} { #pass through to shellspy commandline processor #puts stdout "main.tcl launching app-shellspy" package require app-shellspy } else { #punk shell #todo logger ? #puts stdout "main.tcl launching app-punk. pkg names count:[llength [package names]]" #puts ">> $::auto_path" #puts ">>> [tcl::tm::list]" #puts ">>>> [package unknown]" package require app-punk #app-punk starts repl #repl::start stdin -title "main.tcl" } }} {*}$::argv