** The Proper Rational API for C to Tool Command Language ** Practcl is an alternative to critcl. Practcl is designed to be backfit into standard TEA extensions. Practcl generates blocks of C code and makefile snippets. How those bits fit into the final project are left to the developer. Part of [Tcllib] as of version 1.19 [https://core.tcl.tk/tcllib/doc/tcllib-1-19/support/releases/history/README-1.19.md] ***Documentation:*** http://fossil.etoyoc.com/fossil/odielib/doc/tip/modules/practcl/practcl.md%|%Practcl Markdown Documentation%|% https://www.tcl.tk/community/tcl2016/assets/talk42/practcl-paper-v1.0.1.pdf%|%Tcl'2016 paper%|% Abstract: === Practcl is a library of tools for assembling Tcl distributions, from within a Tcl script. It utilizes exec combined with an object oriented recipe system to download, configure, compile, package, and install extensions. Practcl also includes a build system, a markup language for generating C code, and facilities to link executables, dynamic libraries, and static libraries. === ***Implementation:*** The practcl system is shipped as a self-contained Tcl script. The current version require Tcl8.6. Its inner workings require TclOO. ***Example project:*** The Odielib project uses practcl. At the tail end of configure.ac is the following snippet: ====== AC_SUBST(TEA_PLATFORM) AC_SUBST(TEA_WINDOWINGSYSTEM) AC_OUTPUT([Makefile pkgIndex.tcl project.rc]) ${TCLSH_PROG} ${srcdir}/library.ini ====== The TEA_PLATFORM and TEA_WINDOWINGSYSTEM are computed by the tcl.m4 script, but not normally exported. "project.rc" simply takes in all of the data that is normally substituted into the Makefile, but stores it in a Tcl friendly format. The final line has the configure script invoke the tcl interpreter detected by the tcl.m4, and evaluate the library.ini markup script. library.ini looks like: ====== source project.rc set CWD [pwd] set SRCPATH [file normalize [file join $CWD [file dirname [info script]]]] set SANDBOX [file dirname $SRCPATH] package require practcl 0.1.5 # Create the main ODIELIB object ::practcl::library create ODIELIB { name odielibc pkg_name odielibc pkg_vers 2.2 output_tcl odielibc.tcl output_h odielibc.h output_c odielibc.c output_mk odielibc.mk init_funct Odielibc_Init pkginit odielibc libs {} } # Input markup for the sub-modules of the project ODIELIB add [file join $SRCPATH cmodules btree module.ini] ODIELIB add [file join $SRCPATH cmodules odieutil module.ini] ODIELIB add [file join $SRCPATH cmodules geometry module.ini] # Drop in some top-level settings ODIELIB define add public-include ODIELIB define add public-include ODIELIB define add public-include ODIELIB define add public-include ODIELIB define add public-include ODIELIB define add public-include ODIELIB define add public-verbatim [file join $SRCPATH cmodules odieutil odieutil.h] ODIELIB define add public-verbatim [file join $SRCPATH cmodules btree tree.h] ODIELIB define add include_dir $CWD # Cry havoc, and unleash the dogs of war ODIELIB implement $CWD ====== Within the individual modules, module objects can define * static C files to be compiled * Snippets of dynamic C to be produced at ./configure time * both The cmodules/btree/module.ini file looks like: ====== set here [file dirname [file normalize [info script]]] my add class csource filename [file join $here tree.c] initfunct Tree_Init my add class cheader filename [file join $here tree.h] ====== Whereas the cmodules/geometry/module.ini file is a little more complex. ====== set here [file dirname [file normalize [info script]]] my define add include_dir $here my define set output_c odiegeometry.c my define set output_h odiegeometry.h my define set loader-funct Odie_Geometry_Init my include [my define get output_h] set loaded {} foreach {file} { memory.tcl listcmd.tcl constant.tcl logicset.tcl cmatrix.tcl cmatrixforms.tcl objtypes.tcl linklist.tcl mathtools.tcl polygon.tcl segset.tcl shapes.tcl affine2d.tcl affine3d.tcl vector.tcl vectorn.tcl vector2d.tcl vector3d.tcl vectorxy.tcl vectorxyz.tcl plotter.tcl slicer.tcl wallset.tcl } { lappend loaded [file join $here $file] set obj [my add class submodule filename [file join $here $file]] } foreach file [glob [file join $here *.tcl]] { if {$file ni $loaded} { puts "Forget $file" } } ====== You will note that within the module files, the principle command invoked is "my". That is because module.ini is actually running inside of an object that will implement the module. Likewise, the individual .tcl files in the geometry/module.ini file ALSO operates inside of sub-module objects. <>TEA| Critcl | Development