The icmake scripting language



icmscript - The C-like icmake scripting language


Icmake(1) is a generic tool handling program maintenance that can be used as an alternative for make(1). It's a generic tool in that icmake-scripts, written in a language closely resembling the C programming language, can perform tasks that are traditionally the domain of scripting languages.

Icmake allows programmers to use a programming language (closely resembling the C-programming language) to define the actions that are required for (complex) program maintenance. For this, icmake offers various special operators as well as a set of support functions that have shown their usefulness in program maintenance.

This man-page covers the icmake scripting language in de following sections:


Icmake supports the following five data and value types:

Variables can be defined at the global level inside functions (not only at the top of compound statements but also between statements and in the initialization section of for- and if-statements). When defined inside functions, the standard C scoping and visibility rules apply. Variables are strongly typed, and cannot have type void.

Variables may be initialized when they are defined. Initializations are expressions which may use predefined or user-defined functions, constant values, and values of variables. Functions or variables that are used for initialization must be visible at the initialization point.


Icmake scripts require a user-defined function main. The function main has three optional parameters, which may be omitted from the last one (envp) to the first one (argc), like in C. Its full prototype is:

    void main(int argc, list argv, list envp)

    int main(int argc, list argv, list envp)
When a void main function ends (using a return; statement or when its execution reaches its body's closing curly) the value 0 is returned to the operating system. When int main functions end using return statements then those statements must be provided with int-expressions. It's OK when the execution of an int main function reaches its body's closing curly, om which case 0 is automatically returned to the operating system

In main the parameter

Example (the implementations of the user-defined functions usage, modified, and compile are left as an exercise for the reader):

    void main(int argc, list argv)
        if (argc == 1)

if (list toCompile = modified("*.cc")) { for (int idx = listlen(toCompile); idx--; ) compile(toCompile[idx]); } } ) When executing an icmake script icmake's run-time support system first initializes all all global variables in the order of their definitions. Followin this the function main is called. The script ens once once main returns or when the (predefined) function exit is called by the script).


Before actually compiling icmake scripts they are first pre-processed by the icmake pre-processor. The pre-processor removes comment, includes files specified by include-directives, and processes #define and comparable directives.

The following preprocessor directives are recognized:


The following predefined int constants are available (the functions listed in the intended for column are described in the upcoming sections covering the predefined functions):

symbol value intended for

O_ALL 8 makelist
O_DIR 2 makelist
O_FILE 1 makelist
O_SUBDIR 4 makelist

OFF 0 echo
ON 1 echo

P_CHECK 0 system calls
P_NOCHECK 1 system calls

S_IEXEC 32 stat
S_IFCHR 1 stat
S_IFDIR 2 stat
S_IFREG 4 stat
S_IREAD 8 stat
S_IWRITE 16 stat

The following constants are architecture dependent:

symbol 1 when defined on the platform, otherwise 0

unix Unix, usually with GNU's gcc compiler
UNIX may alternatively be available
linux x86 running Linux (usually with gcc)
LINUX may alternatively be available
M_SYSV, M_UNIX x86 running SCO/Unix
_POSIX _SOURCE Unix with Posix compliant compiler
__hpux HP-UX, with the native HP compiler


Since icmake version 10.00.00 the << operator can be used like the C++ insertion operator. See the description of the functions printf and fprintf below.


All C operators (including the ternary operator) are available (except for pointer operators, as icmake does not support pointers). They operate like their C-programming language's counterparts. Comparison operators return 1 if the comparison is true, otherwise 0 is returned.


For string variables and/or constants the following operators are available (lhs and rhs are string variables or constants):


For list variables and/or values the following operators are available:


Type-casts using the standard C-style cast-operator can be used to cast:


Icmake offers a subset of C's flow control statements. They can be used as in the C programming language.


Icmake provides the following predefined functions, which can be used anywhere in icmake scripts. In the following overview the functions are ordered by categories, and within categories they are ordered alphabetically by function name.

Five categories are distinguished:







In addition to main additional functions are usually defined. Once defined, they can be called. Forward referencing of either variables or functions is not supported, but calling functions recursively is. As function declarations are not supported indirect recursion cannot be used.

User-defined functions must have the following elements:

Function bodies may contain variable definitions (optionally initialized at their definitions). Variable definitions start with a type name, followed by one or more comma separated and optionally initialized variable identifiers.

If a variable is not explicitly initialized it is initialized by default: int variables are initialized to 0, string variables are initialized to empty strings ("") and list variables are initialized to empty lists.

Function bodies may also contain zero or more statements (cf. section FLOW CONTROL). Note that variables may be defined (and optionally initialized) anywhere inside functions where expression statements can be used, and also in the condition sections of if, for, and while statements and in the initialization sections of if andd for statements.


In the following example all C++ source files in the current directory are compiled unless their object files are more recent. The main function creates a list of source files and then passes each of them to a function inspect. That function inspects whether the source file is younger than its object file, and if so it calls compile. The function compile uses exec to call the compiler. If a compilation fails the script stops so the error can be repaired. Source files for which the compilation succeeded are not recompiled when the script is rerun. Assuming the script is named compile.im then it can be called using icmake -s compile.im. This also creates compile.bim, so after the -s call the command icmake -e compile.bim can be used to immediately execute the bim-file:

    void compile(string src)
        exec("g++ -c " + src);      // compile 'src'

    void inspect(string src)
    {                               // get the obj-file's name:
                                    // only compile if necessary
        if (src younger change_ext(src, ".o"))

    int main()
    {                               // find all .cc source files
        list sources = makelist("*.cc");

        for (                       // visit all source files
            int idx = 0, end = listlen(sources); 
                idx != end;
            inspect(sources[idx]);  // compile if needed


icmake(1), icmbuild(1), icmconf(7), icmstart(1), icmstart.rc(7)


Standard comment starting on lines containing preprocessor directives may not extend over multiple lines.

Path names containing blanks are not supported.


This is free software, distributed under the terms of the GNU General Public License (GPL).


Frank B. Brokken (f.b.brokken@rug.nl).