Writing packages using a self-contained Tcl script is one thing, but what about more complex packages with external source files?
As an example, we'll look at making Eric Young's implementation of Blowfish encryption library [7] available as a Tcl package.
Firstly, we put the C source and headers for blowfish into a separate directory blowfish_c:
$ ls blowfish_c bf_cfb64.c bf_enc.c bf_locl.h bf_pi.h bf_skey.c blowfish.h
Note that these files are unchanged from the original distribution.
Now, we create the blowfish.tcl Critcl script to create Tcl commands that invoke the Blowfish library functionality. We’ll also start to use some of the more advanced Critcl facilities.
Firstly, we need to get the Critcl package, but we’ll also check to see that compiling is supported by Critcl on the current platform:
package require critcl if {![critcl::compiling]} { puts stderr "This package cannot \ be compiled without \ critcl enabled" exit 1 }
package provide blowfish 0.10
The critcl::compiling procedure returns 1 if CriTcl is able to find a suitable compiler on the current system. There is a companion procedure critcl::scripting which returns the inverse of critcl::compiling. Why? Depending on what you are doing it helps make some scripts (maybe even the one above) more readable.
Now we declare the C sources and headers needed when compiling blowfish:
critcl::cheaders lets Critcl know that the specified files will be required when compiling the package, and will need to be copied to the Critcl cache.
critcl::csources specifies files that need to be compiled by Critcl when the package is build.
critcl::ccode is used to inject the specified C code into the generated C source file. In this case, the code ensures that the blowfish library declarations are included before compiling any C functions.
Finally we create a C function to implement the blowfish Tcl command (this has been abbreviated - for the full listing see Appendix 1). We do this using the critcl::ccommand procedure, which is similar to critcl::cproc but at a lower level - it ties C code to an objectified Tcl command (via the Tcl_CreateObjCommand function) without any further wrapping:
Note that there are other tools that allow you to build interfaces between Tcl and external libraries, in particular, the SWIG - Simplified Wrapper and Interface Generator [8].
SWIG provides a greater level of automation than Critcl. When using SWIG the developer prepares a small interface file that specifies what functions are to be wrapped. This is used by SWIG to generate the “glue” between an external library and Tcl - the equivalent of the critcl::ccommand is generated for you. On the other hand Critcl is significantly easier to deploy, and generates a package structure ready to use.