2. Specifying API Structure
The basic form of the tspec description of an API has already been explained in section 2.2 - it is a directory containing a set of files corresponding to the headers in that API. Each file basically consists of a list of the objects declared in that header. Each object specification is part of a tspec construct. These constructs are identified by keywords. These keywords always begin with +
to avoid conflict with C identifiers. Comments may be inserted at any point. These are prefixed by #
and run to the end of the line.
In addition to the basic object specification constructs, tspec also has constructs for imposing structure on the API description. It is these constructs that we consider first.
2.1. +SUBSET
A list of tspec constructs within a header can be grouped into a named subset by enclosing them within:
+SUBSET "name" := { .... } ;
where name
is the subset name. These named subsets can be nested, but are still regarded as subsets of the parent header.
The intended use for nested subsets is to provide a mechanism by which +IMPLEMENT
and +USE
may reference specific sections of APIs. Consider the following fictional APIs, Z
, A
and C
:
The Z
API's s.h:
+TYPE (unsigned) w; +SUBSET "subset1" := { +TYPE (unsigned) x; +TYPE (unsigned) z; +SUBSET "subset2" := { +TYPE (unsigned) y; } }
The A
API:
+IMPLEMENT "Z", "s.h", "subset1";
The B
API:
+IMPLEMENT "Z", "s.h", "subset2";
Here the Z
API contains w
, x
, y
and z
. The A
API contains x
, y
and z
from the Z
API. The B
API contains only y
. The +IMPLEMENT
keyword is discussed in more detail below.
Subsets are intended to give a layer of resolution beyond that of the entire header (see section 2.1). Each subset is mapped onto a separate pair of output files, so unwary use of subsets is discouraged.
2.2. +IMPLEMENT and +USE
tspec has two import constructs which allow one API, or header, or subset of a header to be included in another. The first construct is used to indicate that the given set of objects is also declared in the including header, and takes one of the forms:
+IMPLEMENT "api" ; +IMPLEMENT "api", "header" ; +IMPLEMENT "api", "header", "subset" ;
The second construct is used to indicate that the objects are only used in the including header, and take one of the forms:
+USE "api" ; +USE "api", "header" ; +USE "api", "header", "subset" ;
For example, posix:stdio.h
is an extension of c/c89:stdio.h
, so, rather than duplicate all the object specifications from the latter in the former, it is easier and clearer to use the construct:
+IMPLEMENT "c/c89", "stdio.h" ;
and just add the extra objects specified by POSIX. Note that this makes the relationship between the APIs c/c89
and posix
absolutely explicit. tspec is as much concerned with the relationships between APIs as their actual contents.
Objects which are specified as being declared in more than one header of an API should also be treated using +IMPLEMENT
. For example, the type size_t
is declared in a number of c/c89
headers, namely stddef.h
, stdio.h
, string.h
and time.h
. This can be handled by declaring size_t
as part of a named subset of, say, c/c89:stddef.h
:
+SUBSET "size_t" := { +TYPE (unsigned) size_t ; } ;
and including this in each of the other headers:
+IMPLEMENT "c/c89", "stddef.h", "size_t" ;
Another use of +IMPLEMENT
is in the master.ts
file used to list the headers in an API (see section 2.2). This basically consists of a list of +IMPLEMENT
commands, one per header. For example, with c/c89
it consists of:
+IMPLEMENT "c/c89", "assert.h" ; +IMPLEMENT "c/c89", "ctype.h" ; .... +IMPLEMENT "c/c89", "time.h" ;
To illustrate +USE
, posix:sys/stat.h
uses some types from posix:sys/types.h
but does not define them. To avoid the user having to include both headers it makes sense for the description to include the latter in the former (provided there are no namespace restrictions imposed by the API). This would be done using the construct:
+USE "posix", "sys/types.h" ;
On the command-line tspec is given one set of objects, be it an API, a header, or a subset of a header. This causes it to read that set, which may contain +IMPLEMENT
or +USE
commands. It then reads the sets indicated by these commands, which again may contain +IMPLEMENT
or +USE
commands, and so on. It is possible for this process to lead to infinite cycles, but in this case tspec raises an error and aborts. In the legal case, the collection of sets read by tspec is the closure of the set given on the command-line under +IMPLEMENT
and +USE
. Some of these sets will be implemented - that it to say, connected to the top level by a chain of +IMPLEMENT
commands - others will merely be used. By default tspec produces output for all these sets, but specifying the -r command-line option restricts it to the implemented sets.
For further information on the +IMPLEMENT
and +USE
commands see section 6.1.