3. Building
3.1. Building APIs
The API checking is one of the more interesting areas of TenDRA. An overview of the process of building APIs is given in . This diagram expands on the top-right and bottom-right quadrants of the TDF compilation phases described in TDF and Portability.
An API consists of abstract specifications of a set of APIs[a] which represent a similar level of abstraction that their respective standards represent. For example, size_t
for the C89 API is defined to be an unsigned arithmetic type, but exactly which type is left to the implementation. See for details on specifying APIs.
These abstract specifications are converted by tspec into API Source and API Includes. The API Includes contain #pragma token
statements which create tokens that correspond to the various things the API defines. Details of these are documented in the tdfc2
guide. These are used later on, during compilation of users' programs.
The generated API Source from tspec contains implementations of just the symbols present in each header for an API (as opposed to all the extensions your system probably provides), guarded by preprocessor conditions. These guards are of a standard form; for example, ssize_t.c
from the posix1
API:
/* AUTOMATICALLY GENERATED BY tspec 2.8 */ #ifndef __WRONG_POSIX1 #ifndef __WRONG_POSIX1_SYS_TYPES_H_SSIZE_T #if #include ( sys/types.h ) #define __BUILDING_TDF_POSIX1_SYS_TYPES_H_SSIZE_T #include <sys/types.h> #endif #endif #ifndef __BUILDING_TDF_POSIX1_SYS_TYPES_H_SSIZE_T #pragma TenDRA no token definition allow #endif #pragma implement interface <../shared/posix1.api/ssize_t.h> #endif
In the API specifications fed to tspec, ssize_t
is a subset; the __WRONG_POSIX1_SYS_TYPES_H_SSIZE_T
guard above is provided so that it may be excluded if your system does not have a compliant implementation of ssize_t
.
Non-compliance for a particular machine is indicated by setting __WRONG_*
macros in the start-up files for that machine. Hence for ULTRIX, which (apparently) has a ssize_t
incompatible to the posix1
API's, defines __WRONG_POSIX1_SYS_TYPES_H_SSIZE_T
and hence ssize_t
is omitted when compiling the tspec API Source into the API TDF Tokens. See Porting TenDRA to Different Operating Systems for a worked example of making use of these macros.
The compiled API TDF Tokens are linked together into .tl
libraries; each library represents an API. As explained above, the contents of these libraries are the intersection of the sets of the things defined in that particular API and what your system provides.
3.2. Production of TDF
Based on the files installed, the process of compilation is outlined in . These steps may be seen by executing tcc -dry
.
C is used as an example here, though similar things apply for any other producer. The main difference would be in API checking.
It is important to note that during compilation the system headers are not used at all. Instead, the various prototypes and such which would be bought in by #include
statements are prepended from the API Includes. These can be seen by running tcc -E
:
clarion% tcc -Yc89 -E hello.c #line 1 "hello.c" ... #line 13 "$PREFIX_TSPEC/TenDRA/include/shared/c89.api/size_t.h" ... #pragma token VARIETY unsigned size_t # size_t #pragma token VARIETY __size_t # __size_t #pragma promote size_t : __size_t #pragma no_def size_t __size_t ... #line 25 "$PREFIX_TSPEC/TenDRA/include/c89.api/stdio.h" #pragma token EXP rvalue : FILE * : stdin # c89.stdio.stdin #pragma token EXP rvalue : FILE * : stdout # c89.stdio.stdout #pragma token EXP rvalue : FILE * : stderr # c89.stdio.stderr ... #pragma token FUNC int ( __local_printf_string, ... ) : printf # c89.stdio.printf #line 3 "hello.c" int main(void) { printf("hello, world\n"); return 0; }
Here I've omitted most of the things <stdio.h>
defines, just to keep the example small.
Note that the contents of the tspec API Includes are portable; they ought to be the same for any system (this should be the case, since they were generated from the tspec API Specification sources, which did not involve anything system-specific). Therefore since these are included verbatim for a given API at the top of a users' C program, we can infer that the TDF capsule produced (foo.j
is itself portable. This is what TenDRA is all about: producing a portable binary. The steps following (namely the call to trans and beyond) may therefore be on a different system than the one on which hello.j
was produced, even though that system may have a different implementation of the APIs used. As long as the target system provides the same subset in its $api.tl
, the code will link and execute as expected. For details on this, see .
3.3. Installation of TDF
The final step of linking also brings in any system-specific libraries which may be required (such as crt0.o
), and of course any user-specified libraries, if given. These are illustrated representatively and their exact details differ per platform.