critcl_package(n)
๐ฆ critcl_package(n) โ C Runtime In Tcl (CriTcl) ๐ฆ
๐ NAME ๐
critcl_package โ CriTcl Package Reference
โจ SYNOPSIS โจ
package require Tcl 8.6
package require critcl ?3.3.1?
package require platform ?1.0.2?
package require md5 ?2?
::critcl::ccode fragment
::critcl::ccommand tclname cname
::critcl::ccommand tclname arguments body ?option value...?
::critcl::cdata tclname data
::critcl::cconst tclname resulttype value
::critcl::cdefines list of glob patterns ?namespace?
::critcl::cproc name arguments resulttype body ?option value...?
::critcl::cproc name arguments resulttype
::critcl::cinit text externals
::critcl::include path
::critcl::api import name version
::critcl::api function resulttype name arguments
::critcl::api header ?glob pattern...?
::critcl::api extheader ?file...?
::critcl::license author ?text...?
::critcl::summary text
::critcl::description text
::critcl::subject ?key...?
::critcl::meta key ?word...?
::critcl::meta? key
::critcl::buildrequirement script
::critcl::cheaders ?arg...?
::critcl::csources ?glob pattern...?
::critcl::clibraries ?glob pattern...?
::critcl::source glob pattern
::critcl::tsources glob pattern...
::critcl::owns glob pattern...
::critcl::cflags ?arg...?
::critcl::ldflags ?arg...?
::critcl::framework ?arg...?
::critcl::tcl version
::critcl::tk
::critcl::preload lib...
::critcl::debug area...
::critcl::check ?label? text
::critcl::checklink ?label? text
::critcl::msg ?-nonewline? msg
::critcl::print ?-nonewline? ?chan? msg
::critcl::compiled
::critcl::compiling
::critcl::done
::critcl::failed
::critcl::load
::critcl::config option ?val?
::critcl::cache ?path?
::critcl::clean_cache ?pattern...?
::critcl::readconfig path
::critcl::showconfig ?chan?
::critcl::showallconfig ?chan?
::critcl::chooseconfig target ?nomatcherr?
::critcl::setconfig target
::critcl::actualtarget
::critcl::buildforpackage ?flag?
::critcl::cnothingtodo file
::critcl::cresults ?file?
::critcl::crosscheck
::critcl::error msg
::critcl::knowntargets
::critcl::sharedlibext
::critcl::targetconfig
::critcl::buildplatform
::critcl::targetplatform
::critcl::cobjects ?glob pattern...?
::critcl::scan path
::critcl::name2c name
::critcl::argnames arguments
::critcl::argcnames arguments
::critcl::argcsignature arguments
::critcl::argvardecls arguments
::critcl::argconversion arguments ?n?
::critcl::argoptional arguments
::critcl::argdefaults arguments
::critcl::argsupport arguments
::critcl::userconfig define name description type ?default?
::critcl::userconfig query name
::critcl::userconfig set name value
::critcl::at::caller
::critcl::at::caller offset
::critcl::at::caller offset level
::critcl::at::here
::critcl::at::get*
::critcl::at::get
::critcl::at::= file line
::critcl::at::incr n...
::critcl::at::incrt str...
::critcl::at::caller!
::critcl::at::caller! offset
::critcl::at::caller! offset level
::critcl::at::here!
::critcl::collect_begin
::critcl::collect_end
::critcl::collect script
::critcl::make path contents
::preload library
๐ธ DESCRIPTION ๐ธ
Welcome to the C Runtime In Tcl โ short: CriTcl โ bestie, it's a whole system for embedding and using C code from inside Tcl scripts (http://core.tcl-lang.org/tcl). ๐
The critcl package is the core of the system. For an overview of the complete system, see Introduction To CriTcl. For the usage of the standalone critcl program, see CriTcl Application. This core package can be used to embed C code into Tcl scripts. It also gives you access to the internals that other parts of the core use, and which are of interest to anyone who wants to understand the internal workings of the core and of the API it provides to the CriTcl Application. Those advanced sections are marked as such so that people who just want to use the package can skip them. ๐ซถ
This package resides in the Core Package Layer of CriTcl.
+----------------+
|Applications |
| critcl |
| critcl::app |
+----------------+
*================*
|Core Packages |
| critcl |
| critcl::util |
*================*
+----------------+
|Support Packages|
| stubs::* |
| md5, platform |
| ... |
+----------------+
๐ API ๐
Tiny note before the documentation starts: instead of repeatedly saying "a Tcl script with embbedded C code" or "a Tcl script containing CriTcl commands", we're just gonna call that a CriTcl script. A file containing a CriTcl script usually has the extension .tcl or .critcl. Periodt. ๐
โจ EMBEDDED C CODE โจ
The following commands append C code fragments to the current module. Fragments show up in the module in the order they are appended, so earlier fragments (variables, functions, macros, etc.) are visible to later fragments. It's giving very "we build the C file in real time" energy. ๐ง
::critcl::ccode fragment โ Appends the C code in fragment to the current module and returns the empty string. See Runtime Behaviour.
::critcl::ccommand tclname cname โ As documented below, except that cname is the name of a C function that already exists.
::critcl::ccommand tclname arguments body ?option value...? โ Appends the code to create a Tcl command named tclname and a corresponding C function whose body is body and which behaves as documented for Tcl's own Tcl_CreateObjCommand (https://www.tcl-lang.org/man/tcl/TclLib/CrtObjCmd.htm).
arguments is a list of zero to four names for the standard arguments clientdata, interp, objc, and objv. The standard default names are used in place of any missing names. This is a more low-level way than critcl::cproc to define a command, as processing of the items in objv is left to the author, affording complete control over the handling of the arguments to the command. See Runtime Behaviour.
Returns the empty string.
Each option may be one of:
-clientdata c-expressionโ Provides the client data for the new command.NULLby default.-delproc c-expressionโ Provides a function pointer of typeTcl_CmdDeleteProc(https://www.tcl-lang.org/man/tcl/TclLib/CrtObjCmd.htm) as the deletion function for the new command.NULLby default.-cname booleanโ If false (the default), a name for the corresponding C function is automatically derived from the fully-qualified tclname. Otherwise, name of the C function is the last component of tclname.
::critcl::cdata tclname data โ Appends the code to create a new Tcl command named tclname which returns data as a ByteArray result. Returns the empty string. ๐
::critcl::cconst tclname resulttype value โ Appends the code to create a new Tcl command named tclname which returns the constant value having the Tcl type resulttype. value can be a C macro or a function call (including the parentheses) to any visible C function that does not take arguments. Unlike critcl::cdata, resulttype can be any type known to critcl::cproc. Its semantics are equivalent to:
cproc $tclname {} $resulttype "return $value ;"
This is more efficient than critcl::cproc since there is no C function generated. Returns the empty string. ๐ฅ
::critcl::cdefines list of glob patterns ?namespace? โ Arranges for C enum and #define values that match one of the patterns in glob patterns to be created in the namespace namespace, each variable having the same as the corresponding C item. The default namespace is the global namespace. A pattern that matches nothing is ignored.
The Tcl variables are created when the module is compiled, using the preprocessor in order to properly find all matching C definitions.
Produces no C code. The desired C definitions must already exist. No cap. ๐
::critcl::cproc name arguments resulttype body ?option value...? โ Appends a function having body as its body, another shim function to perform the needed conversions, and the code to create a corresponding Tcl command named tclname. Unlike critcl::ccommand the arguments and result are typed, and CriTcl generates the code to convert between Tcl_Obj values and C data types. See also Runtime Behaviour.
Returns the empty string.
string option โ Each may be one of:
-cname booleanโ If false (the default), a name for the corresponding C function is automatically derived from the fully-qualified tclname. Otherwise, name of the C function is the last component of tclname.-pass-cdata booleanโ If false (the default), the shim function performing the conversion to and from Tcl level does not pass theClientDataas the first argument to the function.-arg-offset intโ A non-negative integer,0by default, indicating the number of hidden arguments preceding the actual procedure arguments. Used by higher-order code generators where there are prefix arguments which are not directly seen by the function but which influence argument counting and extraction.
string resulttype โ May be a predefined or a custom type. See CriTcl cproc Type Reference for the full list of predefined types and how to extend them. Unless otherwise noted, the Tcl return code is always TCL_OK.
list arguments โ Is a multi-dictionary where each key is an argument type and its value is the argument name. For example:
int x int y
Each argument name must be a valid C identifier. ๐ฆ
If the name is a list with two things in it, then bestie, item one is the name and item two is the default value. That's how you get a (very) limited form of variadic arguments โ through default values. Like:
int {x 1}
Here x is an optional argument of type int with a default value of 1. Cute, right? ๐
โ ๏ธ Important tea: argument conversion is completely bypassed when the argument isn't provided. So if you wrote a custom converter that does validation, it literally never gets the chance to validate that default value. In this case you gotta check the value in the body of the function yourself. No cap. ๐
Each argument type can be a predefined type or a custom one. Go peek at CriTcl cproc Type Reference for the full list of predefined types and how to extend them. โจ
::critcl::cproc name arguments resulttypeโ same as documented below, but you use this one when the C function named name already exists. ๐ซถ::critcl::cinit text externalsโ appends the C code in text and externals, but only after all the other fragments appended by the previously-listed commands, no matter where in the CriTcl script this command sits relative to them. So basically all their content is visible to it. See also Runtime Behaviour.
The C code in text goes into the body of the initialization function of the shared library backing the CriTcl script, and it runs when that library is loaded into the interpreter. It has access to the variable Tcl_Interp* interp, which references the Tcl interpreter currently being initialized.
externals is placed outside and just before the initialization function โ which makes it a great spot for any external symbols the initialization function needs but that shouldn't be reachable by any other parts of the C code. ๐ฅ
Calls to this command are cumulative.
Returns the empty string.
::critcl::include pathโ a convenient little shorthand for:
critcl::code {
#include <${path}>
}
๐ฆ STUBS TABLE MANAGEMENT ๐ฆ
CriTcl versions 3 and later give you critcl::api to create and manipulate stubs tables โ that's Tcl's dynamic linking mechanism handling the resolution of symbols between C extensions. For an intro, see http://wiki.tcl-lang.org/285, and for the details of CriTcl's particular variant, check the section Stubs Tables. ๐
Importing stubs tables (i.e. APIs) from another extension:
::critcl::api import name versionโ adds the following include directives into the CriTcl script and each of its companion.cfiles:
[1] #include <name/nameDecls.h>
[2] #include <name/nameStubLib.h>
Returns an error if name isn't in the search path for the compiler. See critcl::cheaders and the critcl application's -I and -includedir options.
๐ Important: if name is a fully-qualified name in a non-global namespace, e.g.c::stack, then the namespace separators::get converted into underscores (_) in path names, C code, etc. Don't come for me, that's just how it is.
name/nameDecls.h contains the stubs table type declarations, mapping macros, etc., and it may include package-specific headers. See critcl::api header, below. An #include directive is added at the beginning of the generated code for the CriTcl script and at the beginning of each of its companion .c files.
name/nameStubLib.h contains the stubs table variable definition and the function that initializes it. An #include directive for it gets added to the initialization code for the CriTcl script, along with a call to the initializer function. ๐
If name/name.decls accompanies name/nameDecls.h, it should contain the external representation of the stubs table used to generate the headers. That file gets read and the internal representation of the stubs table gets returned for use by the importing package. Otherwise, the empty string is returned.
One possible use? Automatic generation of C code that calls on the functions listed in the imported API. Obsessed. โญ
When generating a TEA package, the names of the imported APIs are used to declare configure options with which the user can declare a non-standard directory for the headers of the API. Any API name is translated into a single configure option --with-name-include.
Declaration and export of a stubs table (i.e. an API) for the CriTcl script:
::critcl::api function resulttype name argumentsโ adds to the public API of the CriTcl script the signature for the function named name, with the signature given by arguments and resulttype. Code gets generated for a.declsfile, the corresponding public headers, and a stubs table usable bycritcl::api import.
arguments is a multidict where each key is an argument type and its value is the argument name, and resulttype is a C type. ๐ธ
::critcl::api header ?glob pattern...?โ each file matching a glob pattern is copied into the directory containing the generated headers, and an#includedirective for it is added toDecls.hfor the CriTcl script. Returns an error if a glob pattern matches nothing. ๐
A pattern for a relative path is resolved relative to the directory containing the CriTcl script.
::critcl::api extheader ?file...?โ like::critcl::api header, but each file should already exist in the external development environment. An#includedirective is added tofooDecls.h, but file is not copied to the package header directory. Also, file is not a glob pattern, because CriTcl has no context (i.e. directory) in which to expand such patterns. ๐
As with the headers for an imported API, an #include directive is added to the generated code for the CriTcl script and to each companion .c file.
In "compile & run" mode the generated header files and any companion headers are placed in the Result Cache subdirectory for the CriTcl script. That directory is added to the include search path of any other package importing this API and building in mode "compile & run". ๐ฅ
In "generate package" mode, -includedir specifies the subdirectory in the package to place the generated headers in. This directory is added to the search paths for header files, making sure a package importing an API finds it if the package exporting that API used the same setting for -includedir. ๐ง
In "generate TEA" mode the static scanner recognizes critcl::api header as a source of companion files. It also uses data from calls to critcl::api import to add support for --with-foo-include options into the generated configure(.in) so that a user can specify custom locations for the headers of any imported API. ๐
๐ PACKAGE META DATA ๐
CriTcl versions 3 and later can create TEApot meta-data to be placed into teapot.txt in a format suitable for use by the TEApot tools [http://docs.activestate.com/activetcl/8.5/tpm/toc.html].
In version 2, some meta data support was already present through ::critcl::license, but that was only used to generate license.txt. Not the whole meal, just a snack. ๐
::critcl::license author ?text...?โ ignored in"compile & run"mode.
In "generate package" mode it provides information about the author of the package and the license for the package.
The text arguments are concatenated to form the text of the license, which is written to license.terms in the same directory as pkgIndex.tcl. If no text is provided, the license is read from license.terms in the same directory as the CriTcl script. ๐
This information takes precedence over any information specified through the generic API ::critcl::meta. It's additionally placed into the meta data file teapot.txt under the keys as::author and license.
::critcl::summary textโ ignored in"compile & run"mode.
In "generate package" mode it places a short, preferably one-line description of the package into the meta data file teapot.txt under the key summary. This information takes precedence over information specified through the generic API ::critcl::meta. โจ
::critcl::description textโ ignored in"compile & run"mode.
In "generate package" mode it places a longer description of the package into the meta data file teapot.txt, under the key description. The data specified by this command takes precedence over any information specified through the generic API ::critcl::meta.
::critcl::subject ?key...?โ ignored in"compile & run"mode.
In "generate package" mode it places each key into the meta data file teapot.txt, under the key subject. This information takes precedence over any information specified through the generic API ::critcl::meta.
Calls to this command are cumulative. ๐
::critcl::meta key ?word...?โ provides arbitrary meta data outside of the following reserved keys:as::author,as::build::date,description,license,name,platform,require subject,summary, andversion. Its behaviour is like::critcl::subjectin that it treats all keys as list of words, with each call providing one or more words for the key, and multiple calls extending the data for an existing key โ if it's not reserved. ๐ฆ
While it is possible to declare information for one of the reserved keys with this command, such data is ignored when the final meta data is assembled and written. Bye. ๐
Use the commands ::critcl::license, ::critcl::summary, ::critcl::description ::critcl::subject, package require, and package provide to declare data for the reserved keys.
The information for the reserved keys as::build::date and platform is automatically generated by critcl itself. No notes.
::critcl::meta? keyโ returns the value in the metadata associated with key. ๐
Used primarily to retrieve the name of the package from within utility packages having to adapt C code templates to their environment. For example, critcl::class uses does this. (Yes, that's the wording, iykyk.)
::critcl::buildrequirement scriptโ gives you control over the capturing of dependencies declared viapackage require. script is evaluated and any dependencies declared within are ignored, i.e. not recorded in the meta data. ๐ โโ๏ธ
๐ CONTROL & INTERFACE ๐
Okay bestie, this cluster of commands is the whole steering wheel of compilation and linking for a CriTcl script ๐ โ they're what controls the details of building it. And here's the โจiconicโจ part: the info you set here is used only to compile/link the object for that CriTcl script. Like, everything for FOO.tcl is kept completely separate from everything for BAR.tcl. No cross-talk, no weird bleed, no cap ๐
::critcl::cheaders ?arg...?
Gives you extra header locations ๐
- Every argument is a glob pattern.
- If an argument begins with
-, it's an argument to the compiler. - Otherwise, the parent directory of each matching path becomes a directory to be searched for header files.
- Returns an error if a pattern matches no files.
- A pattern for a relative path is resolved relative to the directory containing the CriTcl script.
โ ๏ธ Important!!#includelines are not automatically generated for matching header files. You gotta usecritcl::includeorcritcl::ccodeas necessary to add them. Don't come for me when it doesn't compile, I warned you ๐ญ
Calls to this command are cumulative ๐ซถ
::critcl::csources ?glob pattern...?
Matching paths become inputs to the compilation of the current object, along with the sources for the current CriTcl script ๐ธ Returns an error if no paths match a pattern. A pattern for a relative path is resolved relative to the directory containing the CriTcl script. Calls to this command are cumulative โจ
::critcl::clibraries ?glob pattern...?
Provides the link step with extra libraries and library locations ๐ฅ A glob pattern that begins with - gets added as an argument to the linker. Otherwise, matching files get linked into the shared library. Returns an error if no paths match a pattern. A pattern for a relative path is resolved relative to the directory containing the CriTcl script. Calls to this command are cumulative ๐
::critcl::source glob pattern
Evaluates the files matching each glob pattern as scripts ๐ Returns an error if there are no matching files. A pattern for a relative path is resolved relative to the directory containing the CriTcl script. Slay, straightforward, we move.
::critcl::tsources glob pattern...
Tells CriTcl about extra Tcl script files to source when the shared library is loaded ๐ฆ
Matching paths are made available to the generated shared library when it's loaded for the current CriTcl script. Returns an error if a pattern matches no files. A pattern for a relative path is resolved relative to the directory containing the CriTcl script.
Calls to this command are cumulative.
And here's the detail that actually matters: after the shared library has been loaded, the declared files are sourced in the same order that they were provided as arguments. Order = everything, iykyk ๐
::critcl::owns glob pattern...
Lowkey a niche one ๐ Ignored in "compile and run" and "generate package" modes. In "generate TEA" mode, each file matching a glob pattern is a file to be included in the TEA extension โ but one that could not be ascertained as such from previous commands like critcl::csources and critcl::tsources, either because they were specified dynamically or because they were directly sourced. So it's your little manual override list, bestie ๐
::critcl::cflags ?arg...?
Each arg is an argument to the compiler ๐ ๏ธ Calls to this command are cumulative. That's it, that's the whole vibe.
::critcl::ldflags ?arg...?
Each arg is an argument to the linker ๐ Calls to this command are cumulative. Chef's kiss, next.
::critcl::framework ?arg...?
Each arg is the name of a framework to link on MacOS X ๐ This command is ignored if OS X is not the target โ which is honestly so thoughtful, because it means you can specify frameworks unconditionally and not worry about it exploding elsewhere ๐ Calls to this command are cumulative.
::critcl::tcl version
Specifies the minimum version of the Tcl runtime to compile and link the package for. The default is 8.4. Set it and forget it, bestie โจ
::critcl::tk
Arranges to include the Tk headers and link to the Tk stubs ๐ช One line, huge power move.
::critcl::preload lib...
Arranges for the external shared library lib to be loaded before the shared library for the CriTcl script is loaded โฎ๏ธ Calls to this command are cumulative.
Each library FOO is searched for in the directories listed below, in the order listed. The search stops at the first existing path ๐ต๏ธโโ๏ธ
Few notes first, because details are the moment:
platformis the placeholder for the target platform of the package.- The extension
".so"is the placeholder for whatever actual extension is used by the target platform for its shared libraries. - The search is relative to the current working directory.
And now the paths, depending on the exact form of the library name ๐
FOO
FOO.soFOO/FOO.soFOO/platform/FOO.so
PATH/FOO
The exact set searched depends on the existence of directory PATH/FOO. If it exists, critcl searches
FOO.soPATH/FOO/FOO.soPATH/FOO/platform/FOO.so
Otherwise it searches
FOO.soPATH/FOO.soPATH/platform/FOO.so
instead.
/PATH/FOO
Even when specifying FOO with an absolute path, the first path searched is relative to the current working directory. Not a typo, not a glitch, that's just how it goes ๐
FOO.so/PATH/FOO.so/PATH/platform/FOO.so
For developers who want to understand or modify the internals of the critcl package, Preloading functionality explains how preloading is implemented ๐ง
::critcl::debug area...
Specifies what debugging features to activate ๐ Internally, each area is translated into area-specific flags for the compiler, which are then handed over to critcl::cflags. So it's like a cute little shortcut that funnels into somewhere else.
memoryโ specifies Tcl memory debugging.symbolsโ specifies compilation and linking with debugging symbols for use by a debugger or other tool.allโ specifies all available debugging.
Pick your fighter, or just say all and let it eat ๐
๐ INTROSPECTION ๐
These commands control compilation and linking โ but in the "asking questions about it" way, y'know? Main character energy of checking things ๐
::critcl::check ?label? text
Returns true if the C code in text compiles successfully, and false otherwise โ Used to check for availability of features in the build environment. If provided, label is used to uniquely mark the results in the generated log. So good for "does this machine even have this feature" energy ๐ง
::critcl::checklink ?label? text
Like critcl::check but it also links the compiled objects, returning true if the link is successful and false otherwise ๐ If specified, label is used to uniquely mark the results in the generated log.
::critcl::msg ?-nonewline? msg
Scripts using critcl::check and critcl::checklink can use this command to report results ๐ฃ Does nothing in compile & run mode. Tools like the CriTcl Aplication may redefine this command to implement their own message reporting. For example, critcl::app and any packages built on it print messages to stdout.
::critcl::print ?-nonewline? ?chan? msg
Used by the CriTcl internals to report activity ๐จ๏ธ By default, effectively the same thing as ::puts. Tools directly using either the CriTcl package or the CriTcl application package may redefine this procedure to implement their own output functionality.
For example, the newest revisions of Kettle (https://chiselapp.com/user/andreas_kupries/repository/Kettle/index) use this to highlight build warnings. Obsessed with that actually ๐ธ
::critcl::compiled
Returns true if the current CriTcl script is already compiled and false otherwise ๐
This enables a CriTcl script used as its own Tcl companion file (see critcl::tsources) to distinguish between:
- being sourced for compilation in compile & run mode, and
- being sourced from either the result of generate package mode or during the load phase of compile & run mode.
The result is false in the first case and true in the later two cases. See, it's giving self-aware script โจ
::critcl::compiling
Returns true if a working C compiler is available and false otherwise ๐ ๏ธ Short, sweet, useful.
::critcl::done
Returns true when the CriTcl script has been built and false otherwise โ Only useful from within a CriTcl script. This one enables the Tcl parts of a CriTcl script to distinguish between prebuilt package mode and compile & run mode.
See also Modes Of Operation/Use ๐
::critcl::failed
Returns true if the CriTcl script could not be built, and false otherwise ๐ Forces the building of the package if it hasn't already been done, but not its loading. So a CriTcl script can check itself for availability of the compiled components. Only useful from within a CriTcl script. Self-check era, we love to see it ๐ซถ
::critcl::load
Like critcl::failed except that it also forces the loading of the generated shared library, and that it returns true on success and false on failure ๐ค So a CriTcl script can check itself for availability of the compiled components. Only useful from within a CriTcl script.
โ๏ธ BUILD MANAGEMENT โ๏ธ
This one manages global settings โ that's configuration options which are independent of any CriTcl script ๐
Heads up bestie: this command should not be needed to write a CriTcl script. It's a management command which is only useful to the CriTcl Application or similar tools ๐
::critcl::config option ?val?
Sets and returns the following global configuration options:
forcebool โ when false (the default), the C files are not built if there is a cached shared library.linesbool โ when true (the default),#linedirectives are embedded into the generated C code.
Now, about lines โ the tea is that this facility requires the use of a tclsh that provides info frame ๐ Otherwise, no #line directives are emitted. The command is supported by Tcl 8.5 and higher. It is also supported by Tcl 8.4 provided that it was compiled with the define -DTCL_TIP280. An example of such is ActiveState's ActiveTcl. So like, check your tclsh's vibe before you assume the line directives are showing up ๐ญ
Developers of higher-level packages who generate their own C code โ directly, or indirectly through critcl โ should also read the section Advanced: Location management to see how critcl helps them generate their directives. Examples of packages like this ship with critcl itself. See critcl::iassoc and critcl::class. ๐ซถ
tracebool โ When it'sfalse(the default), no code that traces the entry and exit of CriTcl-backed commands in the CriTcl script gets inserted. Inserting that kind of code implicitly activates the tracing facility in general. Seecritcl::cutil. ๐Ipath โ A single global include path to use for all files. Not set by default.combineenum โ controls the object-file suffix / stubs vibe:dynamic(the default) โ Object files have the suffix_pic.staticโ Object files have the suffix_stub.standaloneโ Object files have no suffix, and the generated C files are compiled without using Tcl/Tk stubs. The result are object files usable for static linking into a big shell.
languagestring โ a string setting. (That's literally all this page says for it.) ๐ฌkeepsrcbool โ Whenfalse(the default), the generated".c"files are deleted after the".o"files have been built.outdirdirectory โ The directory where to place a generated shared library. By default, it is placed into the Result Cache.
๐๏ธ RESULT CACHE MANAGEMENT ๐๏ธ
The following commands control the Result Cache. These commands are not needed to simply write a CriTcl script. ๐ซถ
::critcl::cache ?path?โ Sets and returns the path to the directory for the package's result cache. The default location is~/.critcl/[platform::generic]and usually does not require any changes. ๐๏ธ::critcl::clean_cache ?pattern...?โ Cleans the result cache, i.e. removes any and all files and directories in it. If one or more patterns are specified then only the files and directories matching them are removed. ๐งผ
โ๏ธ BUILD CONFIGURATION โ๏ธ
The following commands manage the build configuration, i.e. the per-platform information about compilers, linkers, and their commandline options. These commands are not needed to simply write a CriTcl script. ๐ง
::critcl::readconfig pathโ Reads the build configuration file atpathand configures the package using the information for the target platform.::critcl::showconfig ?chan?โ Converts the active build configuration into a human-readable string and returns it, or ifchanis provided prints the result to that channel.::critcl::showallconfig ?chan?โ Converts the set of all known build configurations from the currently active build configuration file last set withcritcl::readconfiginto a string and returns it, or ifchanis provided, prints it to that channel.::critcl::chooseconfig target ?nomatcherr?โ Matchestargetagainst all known targets, returning a list containing all the matching ones. This search is first done on an exact basis, and then via glob matching. If no known target matches the argument the default is to return an empty list. However, if the booleannomatcherris specified and set an error is thrown usingcritcl::errorinstead.::critcl::setconfig targetโ Configures the package to use the settings oftarget.
๐ ๏ธ TOOL API ๐ ๏ธ
The following commands provide tools like CriTcl Application or similar with deeper access to the package's internals. These commands are not needed to simply write a CriTcl script. ๐งฐ
::critcl::actualtargetโ Returns the platform identifier for the target platform, i.e. the platform to build for. Unlike::critcl::targetplatformthis is the true target, with any cross-compilation information resolved. ๐ฏ::critcl::buildforpackage ?flag?โ Signals whether the next file is to be built for inclusion into a package. If not specified the flag defaults totrue, i.e. building for a package. This disables a number of things in the backend, namely the linking of that file into a shared library and the loading of that library. It is expected that the build results are later wrapped into a larger collection.::critcl::cnothingtodo fileโ Checks whether there is anything to build forfile.::critcl::cresults ?file?โ Returns information about buildingfile, orinfo scriptiffileis not provided. The result in question is a dictionary containing the following items:clibrariesโ A list of external shared libraries and/or directories needed to linkfile.ldflagsโ A list of linker flags needed to linkfile.licenseโ The text of the license for the package thatfileis located in.mintclโ The minimum version of Tcl required by the package thatfileis in to run successfully. A proper Tcl version number.objectsโ A list of object files to link intofile.preloadโ A list of libraries to be preloaded in order to successfully load and usefile.tkโtrueiffilerequires Tk andfalseotherwise.tsourcesโ A list of companion".tcl"files to source in order to load and use the CriTcl scriptfile.logโ The full build log generated by the compiler/linker, including command line data fromcritcl, and other things.exlโ The raw build log generated by the compiler/linker. Contains the output generated by the invoked applications.
::critcl::crosscheckโ Determines whether the package is configured for cross-compilation and prints a message to the standard error channel if so.::critcl::error msgโ Used to report internal errors. The default implementation simply returns the error. Tools like the CriTcl Application are allowed to redefine this procedure to perform their own way of error reporting. There is one constraint they are not allowed to change: The procedure must not return to the caller. ๐จ::critcl::knowntargetsโ Returns a list of the identifiers of all targets found during the last invocation ofcritcl::readconfig.::critcl::sharedlibextโ Returns the file extension for shared libraries on the target platform.::critcl::targetconfigโ Returns the identifier of the target to build for, as specified by either the user or the system.::critcl::buildplatformโ Returns the identifier of the build platform, i.e. where the package is running on.::critcl::targetplatformโ Returns the identifier of the target platform, i.e. the platform to compile for. In contrast to::critcl::actualtargetthis may be the name of a cross-compilation target.::critcl::cobjects ?glob pattern...?โ Like::critcl::clibraries, but instead of matching libraries, each glob pattern matches object files to be linked into the shared object (at compile time, not runtime). If a glob pattern matches nothing an error is returned. Not listed in Control & Interface because it is of no use to package writers. Only tools like the CriTcl Application need it. A pattern for a relative path is resolved relative to the directory containing the CriTcl script. Calls to this command are cumulative.::critcl::scan pathโ The main entry point to CriTcl's static code scanner. Used by tools to implement processing modes like the assembly of a directory hierarchy containing a TEA-lookalike buildystem, etc. Scanspathand returns a dictionary containing the following items:versionโ Package version.orgโ Author(ing organization).filesโ List of the companion files, relative to the directory of the input file.
::critcl::name2c nameโ Given the Tcl-level identifiername, returns a list containing the following details of its conversion to C:- Tcl namespace prefix
- C namespace prefix
- Tcl base name
- C base name
For use by utilities that provide Tcl commands without going through standard commands like critcl::ccommand or critcl::cproc. critcl::class does this. ๐
๐ง ADVANCED: EMBEDDED C CODE ๐ง
For advanced use, the following commands used by critcl::cproc itself are exposed. โจ
::critcl::argnames argumentsโ Given an argument declaration as documented forcritcl::cproc, returns a list of the corresponding user-visible names.::critcl::argcnames argumentsโ Given an argument declaration as documented forcritcl::cproc, returns a list of the corresponding C variable names for the user-visible names. The names returned here match the names used in the declarations and code returned by::critcl::argvardeclsand::critcl::argconversion.::critcl::argcsignature argumentsโ Given an argument declaration as documented forcritcl::cproc, returns a list of the corresponding C parameter declarations.::critcl::argvardecls argumentsโ Given an argument declaration as documented forcritcl::cproc, returns a list of the corresponding C variable declarations. The names used in these declarations match the names returned by::critcl::argcnames.::critcl::argconversion arguments ?n?โ Given an argument declaration as documented forcritcl::cproc, returns a list of C code fragments converting the user visible arguments found in the declaration fromTcl_Obj*to C types. The names used in these statements match the names returned by::critcl::argcnames. The generated code assumes that the procedure arguments start at indexnof theobjvarray. The default is1.::critcl::argoptional argumentsโ Given an argument declaration as documented forcritcl::cproc, returns a list of boolean values indicating which arguments are optional (true), and which are not (false).::critcl::argdefaults argumentsโ Given an argument declaration as documented forcritcl::cproc, returns a list containing the default values for all optional arguments.::critcl::argsupport argumentsโ hand it an argument declaration exactly like the one you'd givecritcl::cproc, and it hands you back a list of C code fragments that define all the supporting types you need. It's the little prep-cook that gets your types ready before the real work starts ๐
๐ CUSTOM BUILD CONFIGURATION ๐
This package ships exactly one command for managing package-specific custom build configuration options โ the kind the developer picks out themselves, not the ones the build system invents behind your back.
::critcl::userconfig define name description type ?default?โ this one declares a custom build configuration option, complete with its description, its type, and an optional default value.
The type is allowed to be either bool, or a list of values. ok so here's the fine print, lowkey important:
- For
bool, the default value โ if you actually specify one โ must be a boolean. Leave it out and it just defaults to true. slay, sensible defaults โจ - For a list of values, the default value โ again, if specified โ must be one of the values actually found in that list. If you don't specify one, it defaults to the first value of the list. first is the moment, periodt.
The description pulls double duty: it works as in-code documentation of what the option means, and is otherwise ignored. But when critcl generates a TEA wrapper, that same description gets used for the configure option derived from the option the command declared. so write it cute, bestie, it's going public ๐
A boolean option FOO gets translated into a pair of configure options โ --enable-FOO and --disable-FOO โ whereas an option whose type is a list of values gets translated into a single configure option, --with-FOO. two doors vs one door, iykyk.
::critcl::userconfig query nameโ asks the database of custom build configuration options for the current.critclfile and returns the chosen value. That could absolutely just be the default, if nothing was ever set via::critcl::userconfig set.
This is the โจ moment โจ where definitions and set values finally meet up, and it's also where the set values get validated against the definition. it's giving reunion episode with a strict bouncer.
::critcl::userconfig set name valueโ this one's for tools to use, like the critcl application itself, to specify values for custom build configuration options.
When this command runs, the only thing that happens is that the association between option name and value gets recorded. Nothing else. Not a single other thing. And that's on purpose, because at the moment the command is invoked the system may have no idea whether an option with that name even exists, nor what its type is. ๐
So all of the validation is deferred (yes, deferred, not "defered", we're being grammatical today ๐
) to the point where the value is actually asked for, via ::critcl::userconfig query.
Which means you can set values for literally any option you like, and that value takes effect only if such an option is both defined and used later on. Schrรถdinger's build option, no cap.
๐ง ADVANCED: LOCATION MANAGEMENT ๐ง
First, a tiny intro for anyone out there asking themselves "okay but what is location management?"
By default, critcl embeds #line directives into the generated C code, so that any errors, warnings and notes the C compiler spits out during compilation point at the .critcl file the faulty code actually came from โ instead of the generated .c file that nobody has ever willingly opened. ๐ญ
This whole facility depends on having a tclsh that provides info frame. No info frame, no #line directives, simple as that. The command is supported by Tcl 8.5 and higher. It's also supported by Tcl 8.4, provided that one was compiled with the define -DTCL_TIP280 โ an example of such a build being ActiveState's ActiveTcl. ๐ธ
Most users genuinely do not care about this feature beyond wanting it to just work and getting proper code references when they read compiler output. fair.
But developers of higher-level packages that generate their own C code should care, so that their generated code carries proper references too. It's especially key for telling apart bugs in code the package itself generated versus bugs in the user's code that's being fed into the package โ if there are any. ๐ต๏ธโโ๏ธ
Examples of such packages ship with critcl itself โ go peek at the implementations of packages critcl::iassoc and critcl::class.
To help those developers out, eight commands are provided to manage location info. Here they all are ๐
A main concept to hold onto: they all operate on a single stored location โ setting it, returning it, and clearing it. And note that this location information is completely independent of the #line directive generation inside critcl itself. two separate vibes, same building.
::critcl::at::callerโ stores the location of the caller of the current procedure as a tuple of file name and linenumber. Any previously stored location gets overwritten, sorry to it. The result of the command is the empty string.::critcl::at::caller offsetโ same as above, but the stored line number is modified by the specified offset. In essence, this is an implicit call ofcritcl::at::incr. sneaky but documented.::critcl::at::caller offset levelโ same as above, except now the level the location information is taken from gets modified too. Level 0 is the caller, -1 its caller, etc. go up the stack, it's a whole elevator ๐::critcl::at::hereโ stores the current location in the current procedure as a tuple of file name and linenumber. Any previously stored location is overwritten. The result of the command is the empty string.
In terms of ::critcl::at::caller, this is equivalent to:
critcl::at::caller 0 1
::critcl::at::get*โ takes the stored location and returns a formatted#linedirective, all ready to be embedded into some C code. The stored location is left completely untouched. Heads up: the directive carries its own closing newline, it's self-sufficient like that. ๐
For nesting to work out properly โ and please let it work out properly โ the recommendation is that these directives always get added to the beginning of a code fragment. That way, if deeper layers add their own directives, those land before ours and end up inactive. End result: the outermost layer generating a directive "wins", i.e. its directive is the one actually used. As it should be. ๐
::critcl::at::getโ just like the above, except it also clears the stored location. one and done.::critcl::at::= file lineโ lets the caller set the stored location to literally anything they want, completely outside of critcl's control. The result of the command is the empty string. you're on your own here, bestie ๐ซถ::critcl::at::incr n...and::critcl::at::incrt str...โ these two let you modify the line number of the stored location incrementally. The increment is given either as a series of integer numbers (incr), or as a series of strings to consider (incrt). With the string version, the delta is the number of line endings found in those strings. count the line breaks, that's the whole trick ๐::critcl::at::caller!,::critcl::at::caller! offset,::critcl::at::caller! offset level, and::critcl::at::here!โ these are the convenience girls ๐ they combinecallerandherewithget, so they store the location and immediately return it formatted as a proper#linedirective. Also note: after you use them, the stored location is cleared. very "use it and lose it" energy.
๐ฆ ADVANCED: DIVERSIONS ๐ฆ
Diversions exist for higher-level packages that generate their own C code, to make their lives easier when they use critcl's commands that generate Embedded C Code.
Those commands normally generate all their C code for the current .critcl file โ which might not be what a higher-level package actually wants. rude, honestly.
With a diversion, the generator output gets redirected into memory instead, and from there the caller can handle and process it however they like before it gets committed to an actual .c file. it's giving detour, but make it useful โจ
An example of such a package ships with critcl itself โ see the implementation of package critcl::class.
To help those developers, three commands are provided for managing diversions and the collection of C code in memory. Here they are:
::critcl::collect_beginโ starts diverting C code collection into memory.
The result of the command is the empty string.
Multiple calls are allowed, and each call opens a new nesting level of diversion. stack 'em up ๐ฅ
::critcl::collect_endโ ends the diversion of C code collection into memory and returns the collected C code.
If multiple levels of diversion are open, the call only closes and returns the data from the last level. one layer at a time, girl.
The command throws an error if no diversion is active โ that's your signal that collect_begin and collect_end got mismatched. the pairing is sacred ๐
::critcl::collect scriptโ the convenience command: runs the script under diversion and returns the collected C code, guaranteeing thatcollect_beginandcollect_endstay correctly paired. let it handle the bookkeeping, you've got enough going on.
๐ ADVANCED: FILE GENERATION ๐
So file generation is related to the diversions from the previous section, but they are absolutely not the same thing. And even so โ like diversions โ this feature is for higher-level packages generating their own C code.
Three examples of utility packages using this facility ship with critcl itself. Go look at the implementations of packages critcl::literals, critcl::bitmap, and critcl::enum. ๐
When a package implementation gets split into pieces, it's often sensible to have a bunch of pure C companion files holding the low-level code โ except those files may need information about the code in the main .critcl file. And those declarations normally aren't exportable, plus using the stub table support doesn't make sense here, because this is all completely internal to the package. it's a whole "no outside visitors" situation.
With the file generation command below, the main .critcl file can generate any number of header files for the C companions to pick up. ๐
::critcl::make path contentsโ creates the file path in a location where the package's C companion files can pick it up by simply including path during their compilation, without interfering with the outer system at all. clean, contained, no mess ๐ธ
The generated file will contain the specified contents. that's it. that's the whole thing.
๐ CONCEPTS ๐
๐ MODES OF OPERATION/USE ๐
CriTcl can be used in three different modes of operation, and they are:
- Compile & Run, and
- Generate Package
- Generate TEA Package
Compile & Run was the original mode, and it's the default for critcl_pkg. It collects the C fragments from the CriTcl script, builds them as needed, and caches the results so that later loads are faster. ๐ฅ
The second mode, Generate Package, was introduced so that (prebuilt) deliverable packages could be created โ packages that don't depend on a build system existing on the target machine, i.e. no C compiler required over there. This was originally done through the experimental Critbind tool, and is now handled by the CriTcl Application, also named critcl. glow-up achieved โจ
Newly introduced with CriTcl version 3 is Generate TEA Package. This mode constructs a directory hierarchy from the package, which can later be built like a regular TEA package, i.e. using:
.../configure --prefix ...
make all isntall
(yes, isntall. that's literally what the page says. we don't fix the typo, we honor it ๐ญ๐)
Okay so real quick before we dive in: about caching results, go peek at the Result Cache section for the deets ๐
๐ธ RUNTIME BEHAVIOUR ๐ธ
So the default vibe of critcl, like, the package itself, is to defer the compilation, linking, and loading of any C code as much as it possibly can โ because that whole operation is expensive, bestie, mostly in the time it eats up ๐ญ. Translation: the C code embedded into a .critcl file gets built only when the first C command or procedure it provides is actually invoked. Not before. Never before. โจ
This part of the system uses bog-standard functionality built right into the Tcl core, iykyk โ specifically the auto_index variable to map from commands to the scripts that provide them, and the unknown command, which uses that info when the command is finally needed ๐
โ ๏ธ A limitation of this behaviour: you can't just useinfo commandsto check whether a critcl-defined command exists. Nope. You also have to search in theauto_indexarray, in case the thing hasn't been built yet. Lowkey annoying but that's the deal ๐
You can change this behaviour using the control command critcl::load. When you invoke it, the building โ including loading of the result โ is forced. And heads up: after this command has been invoked for a .critcl file, further definition of C code in that file is not allowed any longer. Periodt. ๐
๐ฆ FILE MAPPING ๐ฆ
Each .critcl file is backed by a single private .c file containing that code, plus all the boilerplate necessary for its compilation and linking as a single shared library ๐
The Embedded C Code fragments show up in that file in the exact same order they were defined in the .critcl file โ with like, one single exception. The C code you provide via critcl::cinit gets put after all the other fragments. So:
- all fragments have access to the symbols defined by earlier fragments โ
- the
critcl::cinitfragment has access to all of them, regardless of where you placed it in the.critclfile โ
๐ Note: a limitation of the current system is the near impossibility of C-level access between different critcl-based packages. And the issue is not the necessity of writing and sharing the proper extern statements โ no ma'am. The issue is that the management (export and import) of package-specific stubs-tables is not supported. Which means dependent parts have to be forcibly loaded before their user, with all that entails ๐ฌ. See the section Runtime Behaviour for the relevant critcl limitation, and remember that many older platforms do not support the necessary resolution of symbols โ which is, like, the whole reason stubs were invented for Tcl in the first place ๐ซ
โญ RESULT CACHE โญ
Compiling C code is time-consuming, so critcl doesn't only defer it as much as possible (see Runtime Behaviour above), it also caches the results ๐ฅ
Basically: on the first use of a .critcl file FOO.tcl, the resulting object file and shared library get saved into the cache. On future uses of that same file they get reused โ meaning loaded directly, no compilation required โ provided that the contents of FOO.tcl didn't change ๐
The change detection is based on MD5 hashes. A single hash gets computed for each .critcl file, based on the hashes for all C code fragments and configuration options โ i.e. everything that affects the resulting binary ๐ง
As long as the input file doesn't change as per the hash, a previously built shared library found in the cache is reused, bypassing the compilation and link stages entirely. Slay. โจ
The commands to manage the cache live in the section Result Cache Management. But note that they're useful only to tools based on the package, like the CriTcl Application. Package writers have absolutely no need of them, ngl ๐ โโ๏ธ
And as a last note: the default directory for the cache is chosen based on the chosen build target ๐ฏ. Which means the cache can be put on a shared (network) filesystem without having to fear interference between machines of different architectures ๐
๐ PRELOADING FUNCTIONALITY ๐
Okay so the audience of this section is developers who wanna understand and possibly modify the internals of the critcl package and application. Package writers can totally skip this section, bestie, you're free ๐ซถ
It explains how the preloading of external libraries is realized โจ
Whenever a package declares libraries for preloading, critcl will build a supporting shared library providing a Tcl package named preload ๐. This package is not distributed separately โ it ships as part of the package that requires the preload functionality. This support package exports a single Tcl command:
::preload libraryโ which is invoked once per libraries to preload, with the absolute path of that library. The command then loads the library. On windows the command will further use the Tcl command::critcl::runtime::precopyto copy the library to the disk, should its path be in a virtual filesystem which doesn't directly support the loading of a shared library from it.
The command ::critcl::runtime::precopy is provided by the file critcl-rt.tcl in the generated package โ same as the command ::critcl::runtime::loadlib, which generates the ifneeded script expected by Tcl's package management. That generated ifneeded script contains the invocations of ::preload ๐
The C code for the supporting library lives in the file critcl_c/preload.c, which is part of the critcl package ๐ฆ
The Tcl code for the supporting runtime critcl-rt.tcl lives in the file runtime.tcl, which is part of the critcl::app package โญ
๐ CONFIGURATION INTERNALS ๐
Same audience warning as above, babes: this section is for developers who wanna understand and possibly modify the internals of the critcl package and application. Package writers can skip it ๐
It explains the syntax of configuration files and the configuration keys critcl uses to configure its build backend โ i.e. how this part of the system accesses the compiler, the linker, etc. ๐ ๏ธ
๐ก Strongly recommended: open the file containing the standard configurations (path/to/critcl/Config) in the editor of your choice while reading this section of the documentation, and use it as an extended set of examples going beyond the simple defaults shown here ๐
First up: the keys, the meaning of their values, plus examples drawn from the standard configurations distributed with the package โจ Note that when you're writing a custom configuration it is not necessary to specify all the keys listed below โ only those whose default values are wrong or insufficient for the platform in question ๐
versionโ the command to print the compiler version number. Defaults to:
gcc -v
compileโ the command to compile a single C source file to an object file. Defaults to:
gcc -c -fPIC
debug_memoryโ the list of flags for the compiler to enable memory debugging in Tcl. Defaults to:
-DTCL_MEM_DEBUG
debug_symbolsโ the list of flags for the compiler to add symbols to the object files and the resulting library. Defaults to:
-g
includeโ the compiler flag to add an include directory. Defaults to:
-I
tclstubsโ the compiler flag to set USE_TCL_STUBS. Defaults to:
-DUSE_TCL_STUBS
tkstubsโ the compiler flag to set USE_TK_STUBS. Defaults to:
-DUSE_TK_STUBS
threadflagsโ the list of compiler flags to enable a threaded build. Defaults to:
-DUSE_THREAD_ALLOC=1 -D_REENTRANT=1 -D_THREAD_SAFE=1
-DHAVE_PTHREAD_ATTR_SETSTACKSIZE=1 -DHAVE_READDIR_R=1
-DTCL_THREADS=1
noassertโ the compiler flag to turn off assertions in Tcl code. Defaults to:
-DNDEBUG
optimizeโ the compiler flag to specify the optimization level. Defaults to:
-O2
outputโ the compiler flags to set the output file of a compilation. Defaults to:
-o [list $outfile]
โ ๏ธ NOTE the use of Tcl commands and variables here. At the time critcl uses the value of this key, the value of the referenced variable is substituted into it. The named variable is the only variable whose value is defined for this substitution ๐
objectโ the file extension for object files on the platform. Defaults to:
.o
preproc_defineโ the command to preprocess a C source file without compiling it, but leaving#define's in the output. Defaults to:
gcc -E -dM
preproc_enumโ seepreproc_define, except that#define's are not left in the output. Defaults to:
gcc -E
linkโ the command to link one or more object files and create a shared library. Defaults to:
gcc -shared
link_preloadโ the list of linker flags to use when dependent libraries are pre-loaded. Defaults to:
--unresolved-symbols=ignore-in-shared-libs
stripโ the flag to tell the linker to strip symbols from the shared library. Defaults to:
-Wl,-s
ldoutputโ likeoutput, but for the linker. Defaults to the value ofoutput๐link_debugโ the list of linker flags needed to build a shared library with symbols. Defaults to the empty string. One platform requiring this is all variants of Windows, which uses:
-debug:full -debugtype:cv
link_releaseโ the list of linker flags needed to build a shared library without symbols, i.e. a regular build. Defaults to the empty string. One platform requiring this is all variants of Windows, which uses:
-release -opt:ref -opt:icf,3 -ws:aggressive
sharedlibextโ the file extension for shared library files on the platform. Defaults to:
[info sharedlibextension]
platformโ the identifier of the platform used in generated packages. Defaults to:
[platform::generic]
targetโ the presence of this key marks the configuration as a cross-compilation target, and the value is the actual platform identifier of the target. No default. ๐ฏ
The syntax expected from configuration files is governed by the rules below ๐ Again, it is recommended to open the file containing the standard configurations (path/to/critcl/Config) in the editor of your choice while reading this section of the documentation, and use it as an extended set of examples for the syntax ๐
- Each logical line of the configuration file consists of one or more physical lines. In case of the latter the physical lines have to follow each other and all but the first must be marked by a trailing backslash. This is the same marker for continuation lines as used by Tcl itself โจ
- A (logical) line starting with the character
#(modulo whitespace) is a comment which runs until the end of the line, and is otherwise ignored ๐ - the
ifline</> โ any (logical) line that starts with the wordif(whitespace doesn't count, bestie) gets read as Tcl'sifcommand and run exactly like that. Which means it has to follow Tcl's syntax for the command, and yes that can stretch across multiple logical lines, no biggie. It runs in a safe interpreter. ๐ - the
setline</> โ same energy: a (logical) line starting with the wordset(again, modulo whitespace) is treated as Tcl'ssetcommand and executed as such. Follows Tcl's syntax for the command, may span multiple logical lines, and runs in a safe interpreter. ๐ platformvariablevalue</> โ this is how you define a platform-specific configuration variable and its value. The variable has to be the name of one of the configuration keys listed earlier in this section (no making up new ones, ok?), and the platform string identifies which platform the setting is for. Every setting that shares the same identification string together forms the configuration block for that platform. โจplatformwhenexpression</> โ the special one ๐. This marks the platform, and all the settings in its configuration block, as conditional on expression.
If the build platform is not a prefix of platform, nor vice versa, the whole block is just ignored, no drama. Otherwise the expression gets evaluated via expr, in that same safe interpreter used to run any set and if commands found in the configuration file (see above).
If the expression evaluates to true, this configuration block is considered to be the build platform for the host and gets chosen as the default configuration. A big, large example of this feature lives in the handling of OS X in the standard configuration file, where it selects the architectures to build based on the version of the operating system, the available SDK, etc. I.e. it chooses whether the output is universal or not, and whether it's old-style (ix86 + ppc) versus new-style (ix86 32+64) of universality. Literally so much going on. ๐ฆ
platformcopysourceplatform</> โ this copies the configuration variables and values currently defined in the configuration block for sourceplatform over to that of platform, overwriting existing values and creating missing ones. Variables of platform that sourceplatform doesn't define are not touched, they're safe, periodt.
The copied values can be overridden later in the configuration file, so order matters. Multiple copy lines may exist for a platform and can be intermixed with normal configuration definitions. Only the last definition of a variable is used. ๐
variablevalue</> โ last but not least, this defines a default configuration variable and value. ๐
๐ STUBS TABLES ๐
Okay so this section is for the devs out there building extensions that are not based on critcl, yet still wanna interface with stubs as they're understood and used by critcl โ either by exporting their own stubs table to a critcl-based extension, or by importing a stubs table of a critcl-based extension into their own. Both directions, we love to see it. ๐ซถ
To this end we describe the stubs table information of a package foo.
- capitalization matters</> โ note that the differences in the capitalization of "foo", "Foo", "FOO", etc. below demonstrate how to capitalize the actual package name in each context. It's giving attention to detail. ๐
- the directory</> โ all relevant files must be available in a sub-directory
foowhich can be found on the include search paths. foo.decls</> โ the above directory may contain a filefoo.decls. If present, it's assumed to contain the external representation of the stubs table the headers mentioned in the following items are based on.
critcl is able to use such a file to give the importing package programmatic access to the imported API, for automatic code generation and the like. Obsessed. โจ
fooDecls.h</> โ the above directory must contain a header filefooDecls.h. This file declares the exported API. It's used by both exporting and importing packages. It's usually generated, and it must contain (in the order specified):- the declarations of the exported, i.e. public, functions of foo,
- the declaration of structure
FooStubsfor the stub table, - the C preprocessor macros which route the invocations of the public functions through the stubs table.
These macros must be defined if, and only if, the C preprocessor macro USE_FOO_STUBS is defined. Package foo does not define this macro, as it's allowed to use the exported functions directly. All importing packages however must define this macro, to ensure that they do not use any of the exported functions directly, but only through the stubs table. Big rule, no cap. ๐ฅ
- If the exported functions need additional types for their proper declaration, then these types should be put into a separate header file (of arbitrary name) and
fooDecls.hshould contain an#includedirective to this header at the top.
A very reduced, yet also complete example, from a package for low-level random number generator functions, can be found at the end of this section. ๐ง
fooStubLib.h</> โ the above directory must contain a header filefooStubLib.h. This file defines everything needed to use the API of foo. Consequently it's used only by importing packages. It's usually generated, and it must contain (in the order specified):- An
#includedirective fortcl.h, withUSE_TCL_STUBSsurely defined. - An
#includedirective forfooDecls.h, withUSE_FOO_STUBSsurely defined. - A definition of the stubs table variable, i.e.
- An
const FooStubs* fooStubsPtr;
- A definition of the stubs initializer function, like
char *
Foo_InitStubs(Tcl_Interp *interp, CONST char *version, int exact)
{
/*
* Boiler plate C code initalizing the stubs table variable,
* i.e. "fooStubsPtr".
*/
CONST char *actualVersion;
actualVersion = Tcl_PkgRequireEx(interp, "foo", version,
exact, (ClientData *) &fooStubsPtr);
if (!actualVersion) {
return NULL;
}
if (!fooStubsPtr) {
Tcl_SetResult(interp,
"This implementation of Foo does not support stubs",
TCL_STATIC);
return NULL;
}
return (char*) actualVersion;
}
This header file must be included by an importing package exactly once, so that it contains only one definition of both stubs table and stubs initializer function. One. Single. Definition. ๐
The importing package's initialization function must further contain a statement like
if (!Foo_InitStubs (ip, "1", 0)) {
return TCL_ERROR;
}
which invokes foo's stubs initializer function to set the local stub table up. ๐
For a complete example of such a header file, see below, at the end of this section.
- the tea on why this differs โ</> โ the last item above, about
fooStubLib.h, differs from the regular stub stable system used by Tcl. The regular system assumes that a static librarylibfoostub.awas installed by package foo, and links it.
IMVHO critcl's approach is simpler, using only header files found in a single location, vs. header files and static library found in multiple, different locations.
A second simplification is that we avoid having to extend critcl's compiler backend with settings for the creation of static libraries. We love a shortcut that isn't a mess. ๐ฆ
Below is a complete set of example header files, reduced, yet still complete, from a package for low-level random number generator functions:
rngDecls.h</>:
#ifndef rng_DECLS_H
#define rng_DECLS_H
#include <tcl.h>
/*
* Exported function declarations:
*/
/* 0 */
EXTERN void rng_bernoulli(double p, int*v);
typedef struct RngStubs {
int magic;
const struct RngStubHooks *hooks;
void (*rng_bernoulli) (double p, int*v); /* 0 */
} RngStubs;
#ifdef __cplusplus
extern "C" {
#endif
extern const RngStubs *rngStubsPtr;
#ifdef __cplusplus
}
#endif
#if defined(USE_RNG_STUBS)
/*
* Inline function declarations:
*/
#define rng_bernoulli (rngStubsPtr->rng_bernoulli) /* 0 */
#endif /* defined(USE_RNG_STUBS) */
#endif /* rng_DECLS_H */
rngStubLib.h</>:
/*
* rngStubLib.c --
*
* Stub object that will be statically linked into extensions that wish
* to access rng.
*/
#ifndef USE_TCL_STUBS
#define USE_TCL_STUBS
#endif
#undef USE_TCL_STUB_PROCS
#include <tcl.h>
#ifndef USE_RNG_STUBS
#define USE_RNG_STUBS
#endif
#undef USE_RNG_STUB_PROCS
#include "rngDecls.h"
/*
* Ensure that Rng_InitStubs is built as an exported symbol. The other stub
* functions should be built as non-exported symbols.
*/
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLEXPORT
const RngStubs* rngStubsPtr;
/*
*----------------------------------------------------------------------
*
* Rng_InitStubs --
*
* Checks that the correct version of Rng is loaded and that it
* supports stubs. It then initialises the stub table pointers.
*
* Results:
* The actual version of Rng that satisfies the request, or
* NULL to indicate that an error occurred.
*
* Side effects:
* Sets the stub table pointers.
*
*----------------------------------------------------------------------
*/
#ifdef Rng_InitStubs
#undef Rng_InitStubs
#endif
char *
Rng_InitStubs(Tcl_Interp *interp, CONST char *version, int exact)
{
CONST char *actualVersion;
actualVersion = Tcl_PkgRequireEx(interp, "rng", version,
exact, (ClientData *) &rngStubsPtr);
if (!actualVersion) {
return NULL;
}
if (!rngStubsPtr) {
Tcl_SetResult(interp,
"This implementation of Rng does not support stubs",
TCL_STATIC);
return NULL;
}
return (char*) actualVersion;
}
and that's the whole stubs handshake, bestie โ Tcl_PkgRequireEx grabs the version, rngStubsPtr gets filled in, and if the implementation is giving "no stubs for you" energy it bails with TCL_STATIC and a NULL. Clean. โจ
๐ธ EXAMPLES ๐ธ
okay so for actual examples, go read the section "Embedding C" in Using CriTcl. that's where the real demo lives, iykyk ๐
๐ AUTHORS ๐
the iconic trio behind all this:
- Jean Claude Wippler
- Steve Landers
- Andreas Kupries
๐ BUGS, IDEAS, FEEDBACK ๐
ngl, this doc โ and the whole package it's describing โ will absolutely have bugs and other problems. like, guaranteed. ๐ญ so if you find one, please don't suffer in silence:
Report bugs at https://github.com/andreas-kupries/critcl/issues
and bestie, they want your ideas too ๐ซถ โ enhancements for the package, the application, and/or the documentation are all super welcome, and those should go to the exact same place:
https://github.com/andreas-kupries/critcl/issues
same link, both vibes. easy. ๐
๐ฅ KEYWORDS ๐ฅ
for the search-engine girlies, here's the full tag cloud:
- C code
- Embedded C Code
- calling C code from Tcl
- code generator
- compile & run
- compiler
- dynamic code generation
- dynamic compilation
- generate package
- linker
- on demand compilation
- on-the-fly compilation
๐ CATEGORY ๐
Glueing/Embedded C code โ that's the whole lane, no cap ๐
๐ COPYRIGHT ๐
keeping the receipts, exactly as written:
- Copyright (c) Jean-Claude Wippler
- Copyright (c) Steve Landers
- Copyright (c) 2011-2024 Andreas Kupries
okay that's the whole page, bestie โ you made it to the end and honestly? main character energy ๐ this was critcl_package(n), version 3.3.1, living in the doc section. go embed some C, slay, and remember: compile & run is a lifestyle ๐โจ๐ฆ
๐ฆ where this page comes from
- alpine 3.24: critcl-doc 3.3.1-r1 ยท TCL
- Copyright (c) 2011-2024 Andreas Kupries
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms