iffe is a command interpreter that probes the C compilation environment for features. A feature is any file, option or
symbol that controls or is controlled by the C compiler.
iffe tests features by generating and compiling C programs and
observing the behavior of the C compiler and generated programs.
iffe statements are line oriented. Statements may appear in the operand list with the : operand or newline as
the line delimiter. The standard input is read if there are no command line statements or if file.iffe is omitted.
Though similar in concept to autoconf(1) and
config(1), there are fundamental differences. The latter tend to generate
global headers accessed by all components in a package, whereas iffe is aimed at localized, self contained feature testing.
Output is generated in FEATURE/test by default, where test is the base name of file.iffe or the
iffe run file operand. Output is first generated in a temporary file; the output file is updated if it does not exist
or if the temporary file is different. If the first operand is - then the output is written to the standard output and no
update checks are done.
Files with suffixes .iffe and .iff are assumed to contain iffe statements.
iffe input consists of a sequence of statement lines. Statements that span more than one line contain
begin{
as the last operand (where
begin is command specific) and zero or more data lines terminated by a line containing
}end
as the first operand. The statement syntax is: [
name =] [
!]
test[,
test...] [
-] [
arg[,
arg...]] [
prereq ...] [
begin{ ... |
end ...] [= [
default]].
tests and
args may be
combined, separated by commas, to perform a set of tests on a set of arguments.
name = before
test overrides
the default test variable and macro name, and
- after
test performs the test but does not define the test variable
and macro values.
! before
test inverts the test sense for
if,
elif, and
yes{ and
no{
blocks.
prereqs are used when applying the features tests and may be combinations of:
- compiler options
-D*, -L*, etc.
- library references
-l*, *.a, etc. _LIB_name is defined
to be 1 if -lname is a library.
- header references
*.h. _dir_name is defined to be 1 if dir/name
.h is a header, or if dir is omitted, _hdr_name is defined to be 1 if name.h is a header.
- -
- Prereq grouping mark; prereqs before the first - are passed to all feature tests. Subsequent
groups are attempted in left-to-right order until the first successful group is found.
begin{ ...
}end delimit multiline code blocks that override or augment the default code provided by
iffe.
User supplied code blocks should be compatible with the K&R, ANSI, and C++ C language dialects for maximal portability. In
addition to all macro definitions generated by previous tests, all generated code contains the following at the top to hide dialect
differences:
-
- #if defined(__STDC__) || defined(__cplusplus) || defined(c_plusplus)
-
- #define _STD_ 1
-
- #define _ARG_(x) x
-
- #define _VOID_ void
-
- #else
-
- #define _STD_ 0
-
- #define _ARG_(x) ()
-
- #define _VOID_ char
-
- #endif
-
- #if defined(__cplusplus)
-
- #define _BEGIN_EXTERNS_ extern "C" {
-
- #define _END_EXTERNS_ }
-
- #else
-
- #define _BEGIN_EXTERNS_
-
- #define _END_EXTERNS_
-
- #endif
-
- #define _NIL_(x) ((x)0)
-
- #include <stdio.h>
=
default may be specified for the
key,
lib,
mac,
mth and
typ tests. If the test fails
for
arg then
#define arg default is emitted.
key accepts multiple
= default
values; the first valid one is used.
Each test statement generates a portion of a C language header that contains macro defintions, comments, and other text
corresponding to the feature tests. #ifndef _def_name_directory ... #endif guards the generated
header from multiple #includes, where name is determined by either the run statement input file name if any,
or the first test in the first statement, and directory is the basename component of either the run statement
file, if any, or the current working directory. The output file name is determined in this order:
- -
- If the first command line operand is - then the output is written to the standard output.
- --output=file
Output is file.
- set out file
Output is file.
- [run] [directory/]base[.suffix]
Output is FEATURE/
base.
Generated
iffe headers are often referenced in C source as:
#include "FEATURE/file". The
nmake(1) base rules contain metarules for generating
FEATURE/file
from
features/file[
suffix], where
suffix may be omitted,
.c, or
.sh (see the
run
test below). Because
#include prerequisites are automatically detected,
nmake(1) ensures that all prerequisite
iffe headers are generated
before compilation. Note that the directories are deliberately named
FEATURE and
features to keep case-ignorant file
systems happy.
The feature tests are:
- # comment
Comment line - ignored.
- api name YYYYMMDD symbol ...
Emit api compatibility
tests for name and #define symbol symbol_YYYYMMDD when NAME_API is >= YYYYMMDD
(NAME is name converted to upper case). If NAME_API is not defined then symbol maps to the newest YYYYMMDD
for name.
- define name [ (arg,...) ] [ value ]
Emit a
macro #define for name if it is not already defined. The definition is passed to subsequent tests.
- extern name type [ (arg,...) | [dimension]
]
Emit an extern prototype for name if one is not already defined. The prototype is passed to subsequent
tests.
- header header
Emit #include <header> if header
exists. The #include is passed to subsequent tests.
- print text
Copy text to the output file. text is passed to subsequent
tests.
- reference header
If header exists then add #include header
to subsequent tests.
- ver name YYYYMMDD
#define NAME_VERSION YYYYMMDD
(NAME is name converted to upper case).
- cmd name
Defines _cmd_name if name is an executable in one of the
standard system directories (/bin, /etc, /usr/bin, /usr/etc, /usr/ucb). _directory_name is
defined for directory in which name is found (with / translated to _).
- dat name
Defines _dat_name if name is a data symbol in the default
libraries.
- def name
Equivalent to cmd,dat,hdr,key,lib,mth,sys,typ name.
- dfn name
If name is a macro in the candidate headers then a #define name
value statment is output for the value defined in the headers. The definition is #ifndef guarded.
- exp name expression
If expression is a "..." string then name
is defined to be the string, else if the expr(1) evaluation of expression
is not 0 then name is defined to be 1, otherwise name is defined to be 0. Identifiers in expression may
be previously defined names from other iffe tests; undefined names evaluate to 0. If name was defined in a previous
successful test then the current and subsequent exp test on name are skipped. If name is - then the expression
is simply evaluated.
- hdr name
Defines _hdr_name if the header <name.h>
exists. The --config macro name is HAVE_NAME_H.
- if statement ... | elif statement ... |
else | endif
Nested if-else test control.
- iff name
The generated header #ifndef-#endif macro guard is _name
_H.
- inc file [ re ]
Read #define macro names from file and arrange for those
names to evaluate to 1 in exp expressions. If re is specified then macros not matching re are ignored.
- key name
Defines _key_name if name is a reserved word (keyword).
- lcl name
Generates a #include statement for the native version of the header <
name.h> if it exists. Defines _lcl_name on success. The --config macro name is HAVE_
NAME_H. The default re is ^HAVE_ for --config and ^_ otherwise.
- lib name
Defines _lib_name if name is an external symbol in the
default libraries.
- mac name
Defines _mac_name if name is a macro.
- mem struct.member
Defines _mem_member_struct if member
is a member of the structure struct.
- mth name
Defines _mth_name if name is an external symbol in the
math library.
- nop name
If this is the first test then name may be used to name the output file
and/or the output header guard macro. Otherwise this test is ignored.
- npt name
Defines _npt_name if the name symbol requires a
prototype. The --config macro name is HAVE_NAME_DECL with the opposite sense.
- num name
Defines _num_name if name is a numeric constant enum
or macro.
- nxt name
Defines a string macro _nxt_name suitable for a #include
statement to include the next (on the include path) or native version of the header <name.h> if it
exists. Also defines the "..." form _nxt_name_str. The --config macro name is HAVE_NAME_NEXT
.
- one header ...
Generates a #include statement for the first header found
in the header list.
- opt name
Defines _opt_name if name is a space-separated token in
the global environment variable PACKAGE_OPTIONS.
- pth file [ dir ... | { g1
- ... - gn } | < pkg [ver ...] > ]
Defines _pth_file, with embedded /
chars translated to _, to the path of the first instance of file in the dir directories. { ... }
forms a directory list from the cross-product of - separated directory groups g1 ... gn. < ... > forms a
directory list for the package pkg with optional versions. If no operands are specified then the default PATH directories
are used. The --config macro name is NAME_PATH.
- run file
Runs the tests in file based on the file suffix:
- .c
- file is compiled and executed and the output is copied to the iffe output file. Macros
and headers supplied to begin{ ... }end are also supplied to file.
- .sh
- file is executed as a shell script and the output is copied to the iffe output file.
- .iffe or no suffix
file contains iffe statements.
- set option value
Sets option values. The options are described above.
- siz name
Defines _siz_name to be sizeof(name) if name
is a type in any of <sys/types.h>, <times.h>, <stddef.h>, <stdlib.h>. Any . characters in name
are translated to space before testing and are translated to _ in the output macro name.
- sym name
Defines _ary_name if name is an array, _fun_name
if name is a function pointer, _ptr_name if name is a pointer, or _reg_name if name
is a scalar. In most cases name is part of a macro expansion.
- sys name
Defines _sys_name if the header <sys/name.h>
exists. The --config macro name is HAVE_SYS_NAME_H.
- tst name
A user defined test on name. A source block must be supplied. Defines _
name on success. tst - ... is treated as tst - - ....
- typ name
Defines _typ_name if name is a type in any of <sys/types.h>,
<times.h>, <stddef.h>, <stdlib.h>. Any . characters in name are translated to space before
testing and are translated to _ in the output macro name.
- val name
The output of echo name is written to the output file.
- var name
A user defined test on name. A source block must be supplied. Sets the exp
variable _name on success but does not define a macro.
- (expression)
Equivalent to exp - expression.
Code block names may be prefixed by
no to invert the test sense. The block names are:
- cat
- The block is copied to the output file.
- compile
The block is compiled (cc -c).
- cross
The block is executed as a shell script using
crossexec(1) if --cross is on, or on the local host otherwise, and
the output is copied to the output file. Test macros are not exported to the script.
- execute
The block is compiled, linked, and executed. 0 exit status means success.
- fail
- If the test fails then the block text is evaluated by
sh(1).
- link
- The block is compiled and linked (cc -o).
- macro
The block is preprocessed (cc -E) and lines containing text bracketed by <<"
... ">> (less-than less-than double-quote ... double-quote greater-than greater-than) are copied to the
output file with the brackets omitted.
- no
- If the test fails then the block text is copied to the output file. Deprecated: use { if elif
else endif } with unnamed { ... } blocks.
- note
- If the test succeeds then the block is copied to the output as a /* ... */ comment.
- output
The block is compiled, linked, and executed, and the output is copied to the output file.
- pass
- If the test succeeds then the block text is evaluated by
sh(1).
- preprocess
The block is preprocessed (cc -E).
- run
- The block is executed as a shell script and the output is copied to the output file. Succesful test
macros are also defined as shell variables with value 1 and are available within the block. Likewise, failed test macros are
defined as shell variables with value 0.
- status
The block is compiled, linked, and executed, and the exit status is the test outcome, 0
for failure, the value otherwise.
- yes
- If the test succeeds then the block text is copied to the output file. yes{ ... }end
is equivalent to the unnamed block { ... }. Deprecated: use { if elif else endif } with
unnamed { ... } blocks.