Below is the initialization sequence for tclsh 9.0.2 on Windows. I assume Unix will be very similar.
Call tree (9.0.2)
main
setlocale(LC_ALL, C)
Change \ to / in argv[0]
TCL_LOCAL_MAIN_HOOK(argc, argv) ifdef TCL_LOCAL_MAIN_HOOK
TclZipfs_AppHook
| Tcl_FindExecutable
| | Tcl_InitSubSystems
| | | if subsystemsInitialized == 0
| | | | TclInitThreadStorage
| | | | TclInitThreadAlloc
| | | | TclpInitPlatform
| | | | WSAStartup
| | | | TclpGetCodePage
| | | | TclInitDoubleConversion
| | | | TclInitObjSubsystem
| | | | Tcl_InitHashTable(&typeTable...)
| | | | Tcl_RegisterObjType()+ for all the core types
| | | | TclInitIOSubsystem
| | | | TCL_TSD_INIT(&dataKey) only
| | | | TclInitEncodingSubsystem
| | | | Tcl_InitHashTable(&encodingTable)
| | | | Tcl_CreateEncoding()+ for base encodings
| | | | defaultEncoding = systemEncoding = iso8859-1
| | | | TclInitNamespaceSubsystem
| | | endif
| | | TclInitNotifier (Irrespective of subsystemsInitialized value -> per-thread initialization)
| | TclpSetInitialEncodings
| | | Tcl_SetSystemEncoding(Tcl_GetEncodingNameFromEnvironment)
| | TclpFindExecutable
| | | TclSetObjNameOfExecutable
| | | TclSetProcessGlobalValue(&executableName)
| | Tcl_GetNameOfExecutable
| TclZipfs_Init
| | ZipfsSetup
| | Tcl_FSRegister(&zipfsFilesystem)
| | Initialize internal hash tables
| | interp is null so no interp-specific initialization
| TclZipfs_Mount(Mount from application exe. Fails unless static build)
Tcl_SetPanicProc
| Tcl_InitSubsystems (second invocation!) (why does Tcl_SetPanicProc call it?)
| TclInitNotifier (second invocation!) - does not do anything additional
Tcl_CreateInterp
| Tcl_InitSubsystems (third invocation!)
| TclInitNotifier (third invocation!)
| Tcl_InitHashTable(&cancelTable)
| if (commandTypeInit == 0)
| TclRegisterCommandTypeName()+ for proc, ensemble, alias etc.
| endif
| Create and allocate interp
| TclInitLiteralTable
| TclCreateExecEnv
| Tcl_AsyncCreate
| TclResetRewriteEnsemble
| TclInitLimitSupport
| create core commands
| TclInterpInit
| TclSetupEnv
| TclInitEmbeddedConfigurationInformation
| Init platform array and version globals
| TclpSetVariables
| TclGetProcessGlobalValue
| InitializeDefaultLibrary - sets global tclDefaultLibrary
| Tcl_PkgProvide(Tcl)
| TclTommath_Init
| TclOOInit
| TclZlibInit
Tcl_MainEx
| TclpSetInitialEncodings (second invocation!)
| TclpFindExecutable(argv[0]) (second invocation)
| | TclSetObjNameOfExecutable
| | TclSetProcessGlobalValue(&executableName)
| Tcl_InitMemory
| Tcl_GetStartupScript(NULL)
| Tcl_GetStartupScript(&encodingName)
| set ::argv and ::argc
| tcl_interactive = isatty()
| Tcl_Preserve(interp)
| Tcl_AppInit
| | Tcl_Init
| | | TclInitPkgFiles
| | | Tcl_SetAssocData(interp, "tclPkgFiles")
| | | Tcl_EvalEx(tclPreInitScript) if tclPreInitScript not NULL
| | | Tcl_EvalEx(run tclInit script - see below - to find init.tcl and run it. tclInit is application definable)
| | | TclpSetInitialEncodings (third invocation)
| | Registry_Init (if static build)
| | Dde_Init (if static build)
| | Tcl_EvalEx(set tcl_rcFileName) - TODO - why eval, use C API
| Check Tcl_InterpDeleted
| Check Tcl_LimitExceeded
| if TclFullFinalizationRequested() Tcl_CreateExitHandler(FreeMainInterp)
| path = Tcl_GetStartupScript(&encodingName) - have to refetch since appInitProc might have changed it (why get it initially with NULL arg then?)
| if path != NULL
| | Tcl_FSEvalFileEx(path, encodingName) - run script passed on command line
| else (path == NULL)
| | Tcl_SourceRCFile(interp)
| | Tcl_LinkVar("tcl_interactive", &is.tty)
| | while ()
| | | mainLoopProc = TclGetMainLoop()
| | | if mainLoopProc == NULL
| | | read from stdin and run command
| | | else
| | | mainLoopProc()
| | | Tcl_SetMainLoop(NULL)
| | | endif
| | endwhile
| endif
| mainLoopProc = TclGetMainLoop()
| if (mainLoopProc)
| mainLoopProc()
| Tcl_SetMainLoop(NULL)
| endif
| Tcl_EvalObjEx(interp, "exit") if interp not deleted
| Tcl_Exit() (if above does not exit itself)
The init.tcl script that is processed as part of initialization is located as follows by the tclInit script.
The tclInit script constructs a list of scripts each of which
attempts to locate the init.tcl file. Each of these scripts is run in turn
and the init.tcl returned by the first script that succeeds is run.
tclInit script:
if {[namespace which -command tclInit] eq ""} {
proc tclInit {} {
global tcl_libPath tcl_library env tclDefaultLibrary
rename tclInit {}
if {[info exists tcl_library]} {
set scripts {{set tcl_library}}
} else {
set scripts {}
if {[info exists env(TCL_LIBRARY)] && ($env(TCL_LIBRARY) ne {})} {
lappend scripts {set env(TCL_LIBRARY)}
lappend scripts {
if {[regexp ^tcl(.*)$ [file tail $env(TCL_LIBRARY)] -> tail] == 0} continue
if {$tail eq [info tclversion]} continue
file join [file dirname $env(TCL_LIBRARY)] tcl[info tclversion]
}
}
lappend scripts {::tcl::zipfs::tcl_library_init}
if {[info exists tclDefaultLibrary]} {
lappend scripts {set tclDefaultLibrary}
} else {
lappend scripts {::tcl::pkgconfig get scriptdir,runtime}
}
lappend scripts {
set parentDir [file dirname [file dirname [info nameofexecutable]]]
set grandParentDir [file dirname $parentDir]
file join $parentDir lib tcl[info tclversion]} \
{file join $grandParentDir lib tcl[info tclversion]} \
{file join $parentDir library} \
{file join $grandParentDir library} \
{file join $grandParentDir tcl[info tclversion] library} \
{file join $grandParentDir tcl[info patchlevel] library} \
{
file join [file dirname $grandParentDir] tcl[info patchlevel] library}
if {[info exists tcl_libPath]
&& [catch {llength $tcl_libPath} len] == 0} {
for {set i 0} {$i < $len} {incr i} {
lappend scripts [list lindex \$tcl_libPath $i]
}
}
}
set dirs {}
set errors {}
foreach script $scripts {
if {[set tcl_library [eval $script]] eq ""} continue
set tclfile [file join $tcl_library init.tcl]
if {[file exists $tclfile]} {
if {[catch {uplevel #0 [list source $tclfile]} msg opts]} {
append errors "$tclfile: $msg"
append errors "[dict get $opts -errorinfo]"
continue
}
unset -nocomplain tclDefaultLibrary
return
}
lappend dirs $tcl_library
}
unset -nocomplain tclDefaultLibrary
set msg "Cannot find a usable init.tcl in the following directories: "
append msg " $dirs "
append msg "$errors "
append msg "This probably means that Tcl wasn't installed properly. "
error $msg
}
}
tclInit
Most of these locator scripts above are straightforward in that they check environment variables or various file system directories. Execution of the script that checks the zipfs file system is a bit more involved and implemented by the tcl::zipfs::tcl_library_init command which invokes the ZipFSTclLibraryObjCmd C function. This function executes as follows:
ZipFSTclLibraryObjCmd
TclZipfs_TclLibrary
if zipfs_tcl_library_init (optimization added by Sergey)
ScriptLibrarySetup(zipfs_literal_tcl_library)
searchPath = zipfs_literal_tcl_library/encoding
Tcl_EncodingSearchPath(searchPath)
TclSetProcessGlobalValue(&encodingSearchPath, searchPath) BUG [87b69745be] -> overwrites encoding dirs effect in existing interpreters
TclpSetInitialEncodings() (fixes bug [fccb9f322f])
return ScriptLibrarySetup return value
endif
if ZIPFS_APP_MOUNT/tcl_library/init.tcl exists
zipfs_literal_tcl_library = ZIPFS_APP_MOUNT/tcl_library/init.tcl
ScriptLibrarySetup(zipfs_literal_tcl_library)
return ScriptLibrarySetup return value
endif
if not a static build
if ZipfsAppHookFindTclInit(tcl dll) == TCL_OK -> sets zipfs_literal_tcl_library if found
return ScriptLibrarySetup(zipfs_literal_tcl_library)
endif
endif
zipfs_tcl_library_init = 1 (so zipfs search is not repeated irrespective of failure)
