TEA (tclconfig) Source Code

Practcl Retrofit
Login

For existing projects, which already have an established Makefile system this may not be practical or desired. So for our first example we will provide a minimal shim to allow make to invoke practcl.

The example here is a stylized version of the build system for odielib. Bits have been edited for clarity, and may vary slightly from the production code.


# Minimal make.tcl with a single target
    
set CWD [pwd]
set ::project(builddir) $::CWD
set ::project(srcdir) [file dirname [file normalize [info script]]]
set ::project(sandbox)  [file dirname $::project(srcdir)]
    
if {[file exists [file join $CWD .. tclconfig practcl.tcl]]} {
  source [file join $CWD .. tclconfig practcl.tcl]
} else {
  source [file join $SRCPATH tclconfig practcl.tcl]
}

array set ::project [::practcl::config.tcl $CWD]
::practcl::library create LIBRARY [array get ::project]
LIBRARY source [file join $::project(srcdir) library.ini]
LIBRARY implement $::project(builddir)
set fout [open pkgIndex.tcl w]
puts $fout "
#
# Tcl package index file
#
"
puts $fout [LIBRARY package-ifneeded]
close $fout

This make script has no options, not targets, and only has a single task which will:

The library.ini contains instructions for the LIBRARY object. For this package it looks like:

set SRCPATH [file normalize [my define get srcdir]]

my add [file join $SRCPATH cmodules btree module.ini]
my add [file join $SRCPATH cmodules odieutil module.ini]
my add [file join $SRCPATH cmodules geometry module.ini]

my define add public-include <tcl.h>
my define add public-include <assert.h>
my define add public-include <stdio.h>
my define add public-include <stdlib.h>
my define add public-include <string.h>
my define add public-include <math.h>
my define add include_dir [my define get builddir]

You will note the "my" in front of many statements. This is because the script is actually invoked within the namespace of a TclOO object. This object is the master controller for the project.

The add method introduces a new subordinate object. In this case we have added three modules, which themselves will be objects. The add method has an auto-dectection algorithm to pair the appropriate class for a new subordinate by file extension. If we want more control we could have specified:

my add class module filename [file join $SRCPATH cmodules btree module.ini]

Modules, in turn, can have subordinates.

The btree module is straightforward:

set here [file dirname [file normalize [info script]]]
my add [file join $here tree.tcl]

The tree.tcl file, in turn defines data structures and functions we want available within our library. That file contains a stream of commands along the lines of:

my c_structure Tree {
    /* A complete binary tree is defined by an instance of the following
   ** structure
   */
   int (*xCompare)(const void*, const void*); /* Comparison function */
   void *(*xCopy)(const void*);               /* Key copy function, or NULL */
   void (*xFree)(void*);                      /* Key delete function */
   struct TreeElem *top;                      /* The top-most node of the tree */
};

And

my c_function {static void TreeClearNode(TreeElem *p, void (*xFree)(void*))} {
   /* Delete a single node of the binary tree and all of its children */
   if( p==0 ) return;
   if( p->left ) TreeClearNode(p->left, xFree);
   if( p->right ) TreeClearNode(p->right, xFree);
   if( xFree ){
      xFree(p->key);
   }
   Tcl_Free((char *)p);
}

Methods also exist for injecting arbitrary block of C code into specific places in the resulting C file, defining Tcl commands, and building OO classes.

Source files can also be read in from pure C:

my add [file join $here md5.c]

or

my add class csource initfunc Md5_Init filename [file join $here md5.c]