D 2016-06-24T16:21:49.794 L Practcl\sRetrofit U hypnotoad W 4425 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](http://fossil.etoyoc.com/fossil/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: * Read our practcl build rules (in the library.ini file) * generate a *PKGNAME*.mk file (which is a supplementary list of instructions for the Makefile) * generate the pkgIndex.tcl file (which will notifies Tcl about the package and how to load it.) 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 my define add public-include my define add public-include my define add public-include my define add public-include my define add public-include 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](http://fossil.etoyoc.com/fossil/odielib/info/822483fd556b93ba17510c8bbe1a6c5c40d3946b?txt=1&ln=0) 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] Z 2443155f13d03697aa695b5aea167615