TIP 732: Changes to the Tcl script library search path

Login
Author:             Ashok P. Nadkarni <[email protected]>
Tcl-Version:        9.1
State:              Final
Type:               Project
Created:            2025-09-19
Keywords:           Initialization
Tcl-Branch:         tip-732
Vote:               Done
Vote-Summary:       Accepted 6/0/0
Votes-For:          AN, HO, KW, JN, MC, SL
Votes-Against:      none
Votes-Present:      none

Abstract

This TIP proposes

Background

9.0 Tcl initialization

Historically, Tcl has not clearly documented the calls an application must make to initialize Tcl prior to invocation of Tcl functions or creating interpreters. Manpages, TIP 596, and public documentation like the Wiki and authoritative books, collectively suggest Tcl_InitSubSystems, Tcl_FindExecutable, Tcl_CreateInterp, Tcl_SetPanicProc, and TclZipfs_AppHook may all be used to initialize Tcl.

This TIP proposes to mandate calling either Tcl_FindExecutable or TclZipfs_AppHook before using any other Tcl functions. This is motivated by the following considerations.

9.0 interpreter initialization

The Tclsh initialization sequence core wiki page shows the call tree for creation of a Tcl interpreter. At a high level, this two has phases:

In addition to tThis TIP proposes to streamline this latter step which is detailed below. The motivation for the proposed changes is described in a later section.

Tcl executes the following sequence to run an interpreter pre-initialization script, not to be confused with the application initialization script such as .tclshrc or .wishrc.

(1) If the application has registered a pre-initialization script via the call Tcl_SetPreInitScript that is evaluated to initialize the interpreter. The steps below are skipped.

(2) Otherwise, Tcl looks for a procedure called tclInit already defined by the application in the interpreter after the Tcl_CreateInterp call. If found, the script is run and steps below are skipped.

(3) If both (1) and (2) do not hold, Tcl defines its own tclInit procedure which searches for the init.tcl file in several directories as below and sources it. The location of the init.tcl file is also set as the value of the tcl_library global variable in the interpreter. The init.tcl script does the bulk of the work referenced earlier such as setting up auto_path, unknown, encoding directories etc.

This TIP proposes to change the list of directories that Tcl searches in this last step, the result of which is stored in tcl_library and added to the encoding search path.

In Tcl 9.0, this search is implemented by first constructing a list of directories to search. Note the complete list of candidates is constructed before checking if any particular directory contains init.tcl. Directories are added to search path list in the following order:

(3a) If the application has already set the tcl_library variable, its value is added to the search list. Moreover, the remaining search steps 3b-3j are skipped so this will be the only directory in the search path.

(3b) $env(TCL_LIBRARY), $env(TCL_LIBRARY)/tcl9.0 if env(TCL_LIBRARY) defined and not empty.

(3c) The library directory within the ZipFS archive if present

(3d) $tclDefaultLibrary if defined

(3e) [pgkconfig::get scriptdir,runtime] but only if tclDefaultLibrary was not defined.

(3f) $parent/lib/tcl9.0 where parent is [file dirname [file dirname [info name]]]

(3g) $parent/../lib/tcl9.0

(3h) $parent/library

(3i) $parent/../library

(3j) $parent/../tcl9.0/library

(3k) $parent/../tcl9.0.2/library (patchlevel)

(3l) $parent/../../tcl9.0.2/library (patchlevel)

(3j) Every element of the tcl_libPath list if it is defined

(4) Each directory in the list constructed in (3) is examined for the presence of the init.tcl file. If found, it is sourced. On success, the tcl_library variable is set to the directory path and no more directories are examined. If the init.tcl script raises an error, the iteration continues examinining the remaining directories.

(5) The init.tcl script sets up auto_path, encoding directories, unknown handlers for packages and commands, and the auto exec, auto load and auto indexing facilities.

The changes to the current behavior are driven by the following considerations:

Specification

Tcl initialization

Applications must call one of the following functions prior to making any other calls into the Tcl C API.

Additional threads that access Tcl facilities need not call the above functions and may call Tcl_CreateInterp to create interpreters. Threads that call Tcl functions without creating interpreters beforehand must call Tcl_InitSubSystems first. This is require to initialize the Tcl notifier in a thread that has not previously called any of Tcl_FindExecutable, TclZipfs_AppHook or Tcl_CreateInterp.

Tcl_SetPanicProc does what its name suggests but does not initialize Tcl. It does set up the Tcl stubs structure for stubs-enabled applications.

Tcl_MainEx is modified to not locate the executable or initialize encodings as those tasks have already been executed by one of the above functions.

Interpreter initialization

The resulting initialization sequence with the changes above is shown below:

(1) If the application has registered a pre-initialization script via the call Tcl_SetPreInitScript that is evaluated to initialize the interpreter.

(2) Otherwise, Tcl (via the new LocatePreInitScript internal function) searches for the init.tcl file in several directories as listed in (2a)-(2f).

(2a) If the application has already set the tcl_library variable, the search is ended without checking for the presence of an init.tcl file. This follows 9.0 behavior.

(2b) Likewise, if env(TCL_LIBRARY) is defined, search is ended irrespective of the presence of an init.tcl file. This matches 9.0 documentation, not 9.0 behavior.

(2c) The library directory within the ZipFS archive if present as in 9.0

(2d) $tclDefaultLibrary as in 9.0

(2e) [pgkconfig::get scriptdir,runtime]. This is checked regardless of (2d) unlike 9.0

(2f) $parent/lib/tclVERSION where parent is [file dirname [file dirname [info name]]]

(3) If no directory was found in step (2), an exception is raised. Otherwise the tcl_library variable is set to the found directory.

(4) An attempt is made to source the init.tcl file in the directory. If the file does not exist (this can happen with (2a) and (2b) above) an error is raised. Unlike 9.0, if the found init.tcl raises an exception, it is propagated. In either error case, further directories are not searched.

(5) The init.tcl script calls the tcl::InitAutoPath internal command that sets up auto_path. This command, implemented in C, moved from init.tcl script to append to auto_path the values in env(TCLLIBPATH), tcl_library and its parent, and $parent/lib. In the case of safe interpreters, auto_path is initialized to empty if it did not already exist.

The remaining sections of init.tcl that set up handlers for packages and commands, and the auto exec, auto load and auto indexing facilities, are unchanged.

Performance notes

The following performance improvements are note in TIP 732. Note however, the bulk of the improvement comes from moving search path implementation from Tcl to C, not because of changes in the search path itself.

TIP 732:

% timerate {interp delete [interp create]}
939.971 µs/# 1063 # 1063.9 #/sec 999.189 net-ms

Current Trunk:

% timerate {interp delete [interp create]}
1261.00 µs/# 793 # 793.02 #/sec 999.973 net-ms

Implementation notes

Implementation is in the tip-732 branch.

References

  1. TIP 66 describes some aspects of Tcl initialization. While almost a quarter century old, much of its content related to integration with applications still holds.

  2. Tclsh initialization sequence page in the core wiki shows the call tree for tclsh startup in 9.0.2.

Copyright

This document has been placed in the public domain.