www.gibmonks.com

  Previous section   Next section

Practical Programming in Tcl & Tk, Third Edition
By Brent B. Welch

Table of Contents
Chapter 45.  Compiling Tcl and Extensions


The Sample Extension

This section describes the sample extension that is distributed as part of the Tcl Extension Architecture (TEA) standard. The goal of TEA is to create a standard for Tcl extensions that makes it easier to build, install, and share Tcl extensions. The sample Tcl extension is on the CD, and it can be found on the Web at:

ftp://ftp.scriptics.com/pub/tcl/examples/tea/

There is also documentation on the Web at:

http://www.scriptics.com/products/tcltk/tea/

The extension described here is stored in the network CVS repository under the module name samplextension. If you want direct access to the latest versions of Tcl source code, you can learn about the CVS repository at this web page:

http://www.scriptics.com/products/tcltk/netcvs.html

The sample extension implements the Secure Hash Algorithm (SHA1). Steve Reid wrote the original SHA1 C code, and Dave Dykstra wrote the original Tcl interface to it. Michael Thomas created the standard configure and Makefile templates.

Instead of using the original name, sha1, the example uses a more generic name, exampleA, in its files, libraries, and package names. When editing the sample templates for your own extension, you can simply replace occurrences of "exampleA" with the appropriate name for your extension. The sample files are well commented, so it is easy to see where you need to make the changes.

configure.in

The configure.in file is the template for the configure script. This file is very well commented. The places you need to change are marked with __CHANGE__. The first macro to change is:

AC_INIT(exampleA.h)

The AC_INIT macro lists a file that is part of the distribution. The name is relative to the configure.in file. Other possibilities include ../generic/tcl.h or src/mylib.h, depending on where the configure.in file is relative to your sources. The AC_INIT macro necessary to support building the package in different directories (e.g., either tcl8.2/unix or tcl8.2/unix/solaris). The next thing in configure.in is a set of variable assignments that define the package's name and version number:

PACKAGE = exampleA
MAJOR_VERSION = 0
MINOR_VERSION = 2
PATCH_LEVEL =

The package name determines the file names used for the directory and the binary library file created by the Makefile. This name is also used in several configure and Makefile variables. You will need to change all references to "exampleA" to match the name you choose for your package.

The version and patch level support a three-level scheme, but you can leave the patch level empty for two-level versions like 0.2. If you do specify a patch-level, you need to include a leading "." or "p" in it. These values are combined to create the version number like this:

VERSION = ${MAJOR_VERSION}.${MINOR_VERSION}${PATCH_LEVEL}

Windows compilers create a special case for shared libraries (i.e., DLLs). When you compile the library itself, you need to declare its functions one way. When you compile code that uses the library, you need to declare its functions another way. This complicates the exampleA.h header file. Happily, the complexity is hidden inside some macros. In configure.in, you simply define a build_Package variable. The sample defines:

AC_DEFINE(BUILD_exampleA)

This variable is only set when you are building the library itself, and it is only defined when compiling on Windows. We will show later how this is used in exampleA.h to control the definition of the Examplea_Init procedure.

The configure.in file has a bunch of magic to determine the name of the shared library file (e.g., packageA02.dll, packageA.0.2.so, packageA.0.2.shlib, etc.). All you need to do is change one macro to match your package name.

AC_SUBST(exampleA_LIB_FILE)

These should be the only places you need to edit when adapting the sample configure.in to your extension. It is worth noting that the last macro determines which templates are processed by the configure script. The sample generates two files from templates, Makefile and mkIndex.tcl:

AC_OUTPUT([Makefile mkIndex.tcl])

The mkIndex.tcl script is a script that runs pkg_mkIndex to generate the pkgIndex.tcl file. The pkg_mkIndex command is described in Chapter 12. The mkIndex.tcl script is more complex than you might expect because UNIX systems and Windows have different default locations for binary libraries. The goal is to create a pkgIndex.tcl script that gets installed into the arch/lib/package directory, but can find either arch/bin/package.dll or arch/lib/libpackage.so, depending on the system. You may need to edit the mkIndex.tcl.in template, especially if your extension is made of both Tcl scripts and a binary library.

Makefile.in

The Makefile.in template is converted by the configure script into the Makefile. The sample Makefile.in is well commented so that it is easy to see where to make changes. However, there is some first class trickery done with the Makefile variables that is not worth explaining in detail. (Not in a Tcl book, at least!) There are a few variables with exampleA in their name. In particular, exampleA_LIB_FILE corresponds to a variable name in the configure script. You need to change both files consistently. Some of the lines you need to change are shown below:

exampleA_LIB_FILE = @exampleA_LIB_FILE@
lib_BINARIES = $(exampleA_LIB_FILE)
$(exampleA_LIB_FILE)_OBJECTS = $(exampleA_OBJECTS)

The @varname@ syntax is used to substitute the configure variable with its platform-specific name (e.g., libexamplea.dll or libexample.so). The lib_BINARIES variable names the set of libraries built by the "make binaries" target. The _OBJECT variable is a clever trick to allow a generic library make rule, which appears in the Makefile.in template as @MAKE_LIB@. Towards the end of Makefile.in, there is a rule that uses these variables, and you must change uses of exampleA it to match your package name:

$(exampleA_LIB_FILE) : $(exampleA_OBJECTS)
    -rm -f $(exampleA_LIB_FILE)
    @MAKE_LIB@
    $(RANLIB) $(exampleA_LIB_FILE)

You must define the set of source files and the corresponding object files that are part of the library. In the sample, exampleA.c implements the core of the Secure Hash Algorithm, and the tclexampleA.c file implements the Tcl command interface:

exampleA_SOURCES = exampleA.c tclexampleA.c
SOURCES = $(exampleA_SOURCES)

The object file definitions use the OBJEXT variable that is .o for UNIX and .obj for Windows:

exampleA_OBJECTS = exampleA.${OBJEXT}tclexampleA.${OBJEXT}
OBJECTS = $(exampleA_OBJECTS)

The header files that you want to have installed are assigned to the GENERIC_HDRS variable. The srcdir Make variable is defined during configure to be the name of the directory containing the file named in the AC_INIT macro:

GENERIC_HDRS = $(srcdir)/exampleA.h

Unfortunately, you must specify explicit rules for each C source file. The VPATH mechanism is not reliable enough to find the correct source files reliably. The configure script uses AC_INIT to locate source files, and you create rules that use the resulting $(srcdir) value. The rules look like this:

exampleA.$(OBJEXT) : $(srcdir)/exampleA.c
    $(COMPILE) -c '@CYGPATH@ $(srcdir)/exampleA.c' -o $@

The sample Makefile includes several standard targets. Even if you decide not to use the sample Makefile.in template, you should still define the targets listed in Table 45-4 to ensure your extension is TEA compliant. Plans for automatic build environments depend on every extension implementing the standard make targets. The targets can be empty, but you should define them so that make will not complain if they are used.

Table 45-4. TEA standard Makefile targets.
allMakes these targets in order: binaries, libraries, doc.
binariesMakes executable programs and binary libraries (e.g., DLLs).
librariesMakes platform-independent libraries.
docGenerates documentation files.
installMakes these targets in order: install-binaries, install-libraries, install-doc.
install-binariesInstalls programs and binary libraries.
install-librariesInstalls script libraries.
install-docInstalls documentation files.
testRuns the test suite for the package.
dependGenerates makefile dependency rules.
cleanRemoves files built during the make process.
distcleanRemoves files built during the configure process.

Standard Header Files

This section explains a technique you should use to get symbols defined properly in your binary library. The issue is raised by Windows compilers, which have a notion of explicitly importing and exporting symbols. When you build a library you export symbols. When you link against a library, you import symbols. The BUILD_exampleA variable is defined on Windows when you are building the library. This variable should be undefined on UNIX, which does not have this issue. Your header file uses this variable like this:

#ifdef BUILD_exampleA
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLEXPORT
#endif

The TCL_STORAGE_CLASS variable is used in the definition of the EXTERN macro. You must use EXTERN before the prototype for any function you want to export from your library:

EXTERN int Examplea_Init _ANSI_ARGS_((Tcl_Interp *Interp));

The _ANSI_ARGS_ macro is used to guard against old C compilers that do not tolerate function prototypes.

Using the Sample Extension

You should be able to configure, compile and install the sample extension without modification. On my Solaris machine it creates a binary library named exampleA0.2.so, while on my Windows NT machine the library is named exampleA02.dll. The package name is Tclsha1, and it implements the sha1 Tcl command. Ordinarily these names would be more consistent with the file names and package names in the template files. However, the names in the sample are designed to be easy to edit in the template. Assuming you use make install to copy the binary library into the standard location for your site, you can use the package from Tcl like this:

package require Tclsha1
sha1 -string "some string"

The sha1 command returns a 128 bit encoded hash function of the input string. There are a number of options to sha1 you can learn about by reading the source code.


      Previous section   Next section
    Top