Packages in Tcl

Tcl offers a mechanism to look for and create packages—reusable pieces of code, either bundled with Tcl, created as additional projects or code you write and want to use in multiple applications. Packages are meant to provide an easy to use mechanism for using additional libraries—either your library or third-party libraries. Packages are identified using a package name and version.

Package name can be any string, but a good practice is to name the package using lowercase only and using the same name as the namespace used. For example, functionality to work with base64 encoded data is called base64 and all commands are in the base64 namespace.

Versions are one or more numbers separated by dots, where the first leftmost one indicates major versions, and remaining ones indicate additional versioning, with the leftmost being most significant, and the rightmost least significant. Tcl compares versions by separating the numbers using dots and comparing each part as numbers. version 3.2 is considered to be higher than 2.3 and version 10.1 is considered higher than 9.1. Tcl and Tk themselves are versioned in the same way.

Any code can be made into a package and this process is described in more detail later in this chapter.

Package loading

Operations related to the package system are accessible via the package command and its subcommands. In order to load a package, we need to invoke the package require command, specifying a package name and, optionally, the minimum version that is required.

If not specifying a version, the latest one is automatically loaded. If specifying a version to load, the package version that is larger or equal to the required version, with same major version as the required version is loaded. For example, if the package md5 is available in 1.3, 1.4.4, and 2.0.7 versions, then requiring any version would load the 2.0.7 version; and requiring 1.0 would load version 1.4.4.

If you are using ActiveTcl, then its documentation contains a full list of available packages along with documentation for each of the packages. This book only covers a small subset of all available Tcl packages.

Tcl package lookup works by having a variable that lists all directories Tcl should look for packages in. It then goes into each subdirectory of that directory and checks if the pkgIndex.tcl file exists and loads it. A list of these directories is stored in the global auto_path Tcl variable. In order to add a directory, it is enough to add it using lappend ::auto_path /path/to/new/directory.

Creating a package

Creating one or more Tcl packages is a trivial task and requires creating a directory, either in Tcl's lookup path, or in a place that we will later add to the list of directories. Our package can be stored in any directory. We need to have a file called pkgIndex.tcl, which is a script that should invoke the package ifneeded command, along with the name of the package, its version and the Tcl code to invoke to initialize a package. The scripts that provide a package need to invoke package provide command, providing package name and version as arguments.

While it is possible to create the pkgIndex.tcl file manually, Tcl provides a mechanism for building this file, which the authors recommend. Let's assume we want to build a package called mypackage, 1.0 version that will create a namespace called mypackage and an add command in it. What we need to start with is to set up a directory for our test packages and another for this particular package—for this example, we'll assume /tmp/packages and /tmp/packages/mypackage1.0 for the package. First create a file called /tmp/packages/mypackage1.0/mypackage.tcl containing the following code:

namespace eval mypackage {}
proc mypackage::add {a b} {
return [expr {$a + $b}]
}
package provide mypackage 1.0

Next run the following command in any Tcl interpreter:

% pkg_mkIndex /tmp/packages/mypackage1.0

This will cause /tmp/packages/mypackage1.0/pkgIndex.tcl to be created. Every time you change your package names, package versions or filenames, the pkgIndex.tcl file will need to be rebuilt using the previous example.

Now in the same or new session of Tcl interpreter, type in the following commands:

% lappend ::auto_path /tmp/packages
/opt/ActiveTcl-8.5/lib /tmp/packages
% package require mypackage
1.0

More complex packages can include multiple files. If multiple files state that they provide the same package in the same version, then pkgIndex.tcl file will indicate that all those files need to be loaded. If multiple files provide different packages or different versions of the same package, then this will be reflected in the pkgIndex.tcl file as well.

For larger packages, the authors recommend building multiple smaller packages and having one larger package that only requires the remaining ones—for example, mypackage::add and mypackage::sub will provide actual commands and main mypackage command will just load the actual ones.

Tcl modules

Tcl 8.5 also introduces a new concept called Tcl modules. This is a simplification of packaging system we previously mentioned, which assumes that all packages reside in individual files. Tcl will load this file by loading this script, so it either needs to be a Tcl script or it can be a Starkit archive with a native library embedded in it. Starkit archives are introduced in Chapter 3.

The main reason for creating a modules system in Tcl is to be able to clearly map the directory structure to package names, similar to how it is done in languages such as Java or Python—by knowing the package name you can easily find directory and the files that define a particular package.

The path and filename reflect the actual package name and version. For example, the package mypackage with version 1.1 would reside in a file called mypackage-1.1.tm. If the package name contains one or more :: strings, then those are used to map packages to directories. For example, mypackage::common::misc version 1.2 package would need to reside in mypackage/common directory with name misc-1.2.tm.

Loading a module does not differ in any way from loading a package—all we need to do is run the package requiremypackage and Tcl will find and load it in the same way regardless of whether it is a package or module.

Tcl looks for these modules in different set of directories so that the package lookup mechanism is separated from the modules lookup. In order to add a directory where modules should be looked for, we need to invoke the tcl::tm::add command with a directory where files should be looked for. For example, to create a package called mypackage with version 1.1, we can create directory called /tmp/tclmodules and create a file called mypackage-1.1.tm in it with the following contents:

namespace eval mypackage {}
proc mypackage::add {a b} {
return [expr {$a + $b}]
}
package provide mypackage 1.1

Then we can run the following code to test it:

% tcl::tm::add /tmp/tclmodules
% package require mypackage
1.1

Even though, internally, Tcl packages and modules differ slightly from each other in how they are handled, the authors have decided not to distinguish those two concepts where it is not necessary and always use the term package, whether Tcl uses modules or packages to load it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.93.169