A C Runtime In Tcl for on-the-fly compilation ============================================= Rev 0.33: Bumped version to brin in line with critcl.kit Rev 0.32: Improvements by Steve Landers for standalone use Rev 0.31: Fixed so critcl can be used from the interactive prompt Rev 0.30: Allow passing Tcl_Interp*, Tcl_Obj*'s, or return a status Rev 0.29: Redirect gcc stderr to logfile, add some support for "-g" Rev 0.28: Support for app-specific initialization (used in TclKit) Rev 0.27: Commands are now defined in currently active namespace The http://mini.net/tcl/2516.html page is a good spot to comment on this. The C Runtime In Tcl is a self-contained package to build C code into an extension on the fly. It is somewhat inspired by Brian Ingerson's Inline for Perl, but CriTcl is considerably more lightweight. The idea is to wrap C code into something that will compile into a Tcl extension, and then also fire up the compiler. Compiled chunks will be cached in your ~/.critcl/ directory, so subsequent use will be instant. The main definition is "critcl::cproc", it lets you define a (surprise!) C proc, with C code as body. Args and return values must be typed. There are no default args or ways to pass more sophisticated data items than int/long/float/double/char* for now. The return type can be "string", meaning it's a Tcl_Alloc'ed char* which will at some point be Tcl_Free'd. As of rev 0.30, you can also use Tcl_Obj* args (no refcount change), or return it (in which case it will be decref'ed). If the first parameter to a cproc has type "Tcl_Interp*", that'll be passed in. Lastly, if the return type is "ok", then an int return code of type TCL_OK/TCL_ERROR is expected and will be processed as usual (errors must set the result, so it is most likely that you'll also want to specify the Tcl_Interp* arg). Some more commands defined in CriTcl are: critcl::ccode { ... C code ... } inject C code as is (#includes, #defines, common defs, etc) critcl::cinit { ... C code ... } inject C code as is, to be executed at extension-init time critcl::ccommand name {cd ip objc objv} { ... C code ... } ties code to the Tcl_CreateObjCommand without further wrapping critcl::cdata name anydata when called, [name] will return anydata (as byte array) To compile additional source files, you can use the following: critcl::cheaders file ... set up file(s) to be available in compiles (also: "dir/*.h") critcl::csources file ... additional source files passed to the compiler on the cmd line critcl::clibraries file ... additional libraries (args such as "-l..." are passed on as is) GENERATING SHARED LIBS FOR FURTHER USE As of revision 0.22, you can now choose to store compiled binaries in a specific directory and give them a more meaningful name. If file "foo.tcl" uses critcl, then the following sequence can be used: package require critcl critcl::config outdir . source blah/foo.tcl ;# creates & uses "blah/foo.so" Or, use an absolute path to store all compiled libs in a single area: package require critcl critcl::config outdir ~/critcl-linux/ source blah/foo.tcl ;# creates & uses "~/critcl-linux/foo.so" If the libs exist, no re-compilations will be started. Note: there is a key difference with the default case (which creates libs with funny names in the ~/.critcl/ area): SETTING "OUTDIR" DISABLES AUTOMATIC RECOMPILES! If you set outdir, then you must delete shared libs to get them re-built. The CritLib distribution comes with a simple "pkgtest.tcl" script, which can be used to (re-) generate shared libs for all CritLib packages: rm -f ~/mylibs/* pkgtest ~/mylibs Without command-line argument, pkgtest does a normal test run as before. If all libraries are found, then CriTcl will never launch the compiler. This makes it possible to deploy to systems which do not need/have gcc. See also the "critbind" utility, which can generate a single file for easy deployment (as shared lib, static lib, or as complete app). SOLVING COMPILE PROBLEMS The intermediate C code generated by CriTcl can be kept around by doing: critcl::config keepsrc 1 In case of compile errors the source always remains in the ~/.critcl/ directory, but either way it won't be obvious because of the weird file names. To find out what the last compile was doing, look at the end of the log file, which for 0.22 is called "~/.critcl/v022.log". CriTcl inserts a "#line" directive in the generated C source, so that an error on line three of cproc "foo" in script "bar.tcl" will be reported by gcc as occurring on "bar.tcl/foo", line 3. No name is added to ccode sections, so with multiple sections, identifying the line is harder. If "critcl::cheaders -g" is given, then the output file is not stripped and the "-DNDEBUG" flag is not added to the gcc command line. CLEANING UP Over time, the ~/.critcl/ directory could fill up with debris: obsolete builds of your compiled code and builds from previous CriTcl revisions. You can always delete the ~/.critcl/ area - it has no further impact than causing a few re-compiles on next use. The use of Tcl stubs, and the fact that this extension has all include files it needs to make compilation self-contained, means that this is a pure Tcl package, which should work with any (8.1 and up) installation of Tcl. Most importantly, CriTcl does not care a bit where Tcl was installed, nor even whether it was built as a static or as a dynamic executable. This is a working demo, but it is still young. It will for now blindly use "gcc" to do the compile and has somewhat rudimentary error handling. CriTcl has been verified to work on Linux and Win NT4 (MinGW) so far. One more thing: this code assumes the "md5" command is available, either as command or as package. A number of heuristics are used to locate an appropriate implementation, as last resort CriTcl will use "md5pure.tcl" which is now included in CritLib.