A package facility was added in Tcl 7.5. It supports version numbers and has a provide/require model of use. Typically each file in a library provides one package with a particular version number. Packages also work with shared object libraries that implement Tcl commands in compiled code, which are described on page 523. A package can be provided by a combination of script files and object files. Applications specify which packages they require and the libraries are loaded automatically. The package facility is an alternative to the auto loading scheme used in earlier versions of Tcl. You can use either mechanism, and this chapter describes them both.
If you create a package you may which to use the namespace facility to avoid conflicts between procedures and global variables used in different packages. Namespaces are the topic of Chapter 14. Before Tcl 8.0 you had to use your own conventions to avoid conflicts. This chapter explains a simple coding convention for large Tcl programs. I use this convention in exmh, a mail user interface that has grown from about 2,000 to over 25,000 lines of Tcl code. Easily half of the code has been contributed by the exmh user community. Such growth might not have been possible without a module system.
Locating Packages: The auto_path Variable
The package facility assumes that Tcl libraries are kept in well-known directories. The list of well-known directories is kept in the auto_path Tcl variable. This is initialized by tclsh and wish to include the Tcl script library directory, the Tk script library directory (for wish), and the parent directory of the Tcl script library directory. For example, on my Macintosh auto_path is a list of these three directories:
Disk:System Folder:Extensions:Tool Command LanguageDisk:System Folder:Extensions:Tool Command Language:tk4.2
On my Windows 95 machine the auto_path lists these directories:
c:\Program Files\Tcl\lib\Tcl7.6
c:\Program Files\Tcl\libc:\Program Files\Tcl\lib\Tk4.2
On my UNIX workstation the auto_path lists these directories:
/usr/local/tcl/lib/tcl7.6
/usr/local/tcl/lib/usr/local/tcl/lib/tk4.2
The package facility searches these directories and their subdirectories for packages. The easiest way to manage your own packages is to create a directory at the same level as the Tcl library. Packages in this location, for example, will be found automatically because the auto_path list includes /usr/local/tcl/lib:
/usr/local/tcl/lib/welchbookYou can also add directories to the auto_path explicitly:
lappend auto_path directory
package provide name versionThe name identifies the package, and the version has a major.minor format. The convention is that the minor version number can change and the package implementation will still be compatible. If the package changes in an incompatible way, then the major version number should change. For example, Chapter 16 defines several procedures that use the HTTP network protocol. These include Http_Open, Http_Get, and Http_Validate. The file that contains the procedures starts with this command:
package provide Http 1.0More than one file can contribute to the same package simply by specifying the same name and version. In addition, different versions of the same package can be kept in the same directory but in different files.
An application specifies the packages it needs with the package require command:
package require name ?version? ?-exact?If the version is left off, then the highest available version is loaded. Otherwise the highest version with the same major number is loaded. For example, if the client requires version 1.1, version 1.2 could be loaded if it exists, but version 1.0 would not be loaded. You can restrict the package to a specific version with the -exact flag. If no matching version can be found, then the package require command raises an error.
pkg_mkIndex /usr/local/lib/welchbook *.tclThe pkg_mkIndex command sources all the files matched by the pattern, detects what packages they provide, and computes the index. You should be aware of this behavior because it only works well for libraries. If the pkg_mkIndex command hangs or starts random applications, it is because it sourced an application file instead of a library file.
pkg_mkIndex directory *.tcl *.so *.shlib *.dllIn this example, .so, .shlib, and .dll are file suffixes for shared libraries on UNIX, Macintosh, and Windows systems, respectively. You can have packages that have some of their commands implemented in C, and some implemented as Tcl procedures. The script files and the shared library just have to declare that they implement the same package. The pkg_mkIndex procedure will detect this and set up the auto_index so some commands are defined by sourcing scripts, and some are defined by loading shared libraries.
If your file servers support more than one machine architecture, such as Solaris and Linux systems, you probably keep the shared library files in machine-specific directories. In this case the auto_path should also list the machine-specific directory so the shared libraries there can be loaded automatically. If your system administrator configured the Tcl installation properly, this should already be set up. If not, or you have your shared libraries in a non-standard place, you must append the location to the auto_path variable.
When you create a script library without packages, you must generate the index that records what procedures are defined in the library. The auto_mkindex procedure creates the index, which is stored in a file named tclIndex that is kept in the script library directory. Suppose all the examples from this book are in the directory /usr/local/tcl/welchbook. You can make the examples into a script library by creating the tclIndex file:
auto_mkindex /usr/local/tcl/welchbook *.tclYou will need to update the tclIndex file if you add procedures or change any of their names. A conservative approach to this is shown in the next example. It is conservative because it re-creates the index if anything in the library has changed since the tclIndex file was last generated, whether or not the change added or removed a Tcl procedure.
proc Library_UpdateIndex { libdir } {
set index [file join $libdir tclIndex]
if ![file exists $index] {
set doit 1
} else {
set age [file mtime $index]
set doit 0
# Changes to directory may mean files were deleted
if {[file mtime $libdir] > $age} {
set doit 1
} else {
# Check each file for modification
foreach file [glob [file join $libdir *.tcl]] {
if {[file mtime $file] > $age} {
set doit 1
break
}
}
}
}
if { $doit } {
auto_mkindex $libdir *.tcl
}
}Tcl uses the auto_path variable to record a list of directories to search for unknown commands. To continue our example, you can make the procedures in the book examples available by putting this command at the beginning of your scripts:
lappend auto_path /usr/local/tcl/welchbookThis has no effect if you have not created the tclIndex file. If you want to be extra careful, you can call Library_UpdateIndex. This will update the index if you add new things to the library.
lappend auto_path /usr/local/tcl/welchbook
Library_UpdateIndex /usr/local/tcl/welchbookThis will not work if there is no tclIndex file at all because Tcl won't be able to find the implementation of Library_UpdateIndex. Once the tclIndex has been created for the first time, then this will ensure that any new procedures added to the library will be installed into tclIndex. In practice, if you want this sort of automatic update it is wise to include something like the Library_UpdateIndex procedure directly into your application as opposed to loading it from the library it is supposed to be maintaining.
source [file join [info library] init.tcl]
source [file join $dir bind_ui.tcl]
load [file join $dir mime.so] MimeThe $dir gets substituted with the name of the directory that contains the library file, so the result is a source or load command that defines the missing Tcl command. The substitution is done with eval, so you could initialize auto_index with any commands at all. Example 12-2 is a simplified version of the code that reads the tclIndex file.
# This is a simplified part of the auto_load command.
# Go through auto_path from back to front.
set i [expr [llength $auto_path]-1]
for {} {$i >= 0} {incr i -1} {
set dir [lindex $auto_path $i]
if [catch {open [file join $dir tclIndex]} f] {
# No index
continue
}
# eval the file as a script. Because eval is
# used instead of source, an extra round of
# substitutions is performed and $dir gets expanded
# The real code checks for errors here.
eval [read $f]
close $f
}
set auto_noload anything
set auto_noexec anything
Abbreviations
If you type a unique prefix of a command, unknown recognizes it and executes the matching command for you. This is done after automatic program execution is attempted and history substitutions are performed. Tcl Shell Library Environment
It may help to understand how the Tcl shells initialize their library environment. The first toehold on the environment is made when the shells are compiled. At that point the default pathname of the library directory is defined. For Tcl, this pathname is put into the tcl_library variable. This value is also returned by the info library command for backward compatibility with early versions of Tcl:
info libraryAs of Tcl 7.6, another variable is also defined when Tcl is compiled. The tcl_packagePath variable is defined and is used as the initial value of the auto_path variable. It contains a list of the Tcl script library directory, its parent directory, and the directory containing compiled shared libraries. Changing tcl_packagePath has no effect after auto_path is initialized.
A Tcl shell initializes itself by sourcing init.tcl:
source [file join $tcl_library init.tcl]The primary thing defined by init.tcl is the implementation of the unknown procedure. It also initializes auto_path from the value of tcl_packagePath.
The Tk library pathname is defined by the tk_library variable. For Tk, wish also does this:
source [file join $tk_library tk.tcl]This initializes the scripts that support the Tk widgets. There are still more scripts, and they are organized as a library. So, the tk.tcl script sets up the auto_path variable so the Tk script library is accessible. It does this:
lappend auto_path $tk_libraryTo summarize, the bootstrap works as follows:
This naming convention precludes casual names like doit, setup, layout, and so on. There is no way to hide procedure names, so you must maintain the naming convention for all procedures in a package.
A Global Array for State Variables
You should use the same prefix on the global variables used by your package. You can alter the capitalization, just keep the same prefix. I capitalize procedure names and use lowercase for variables. By sticking with the same prefix you identify what variables belong to the package and you avoid conflict with other packages.