icmconf(7)

πŸ’– NAME πŸ’–

icmconf β€” the configuration file for the icmbuild(1) program maintenance script. Yes bestie, that's the whole vibe: it configures things. Keep reading, it gets so much better.


πŸŽ€ DESCRIPTION πŸŽ€

Okay so here's the tea: the icmconf configuration file is what you use to configure the program maintenance that icmbuild(1) performs. It's packed with all these directives, and those directives are the ones that decide how your program or library maintenance actually goes down.

icmconf's directives are biased towards building C++ programs β€” that's her main character energy β€” but ngl you can absolutely configure program maintenance for other languages too (C, for example). It's giving versatile.

Now, the important rules of the game, and you cannot skip these:

  • icmbuild(1) ignores empty lines.
  • It also ignores lines whose first non-blank characters are two consecutive forward slashes (//), or lines that start with a hash-character (#).
  • Long lines can be split over multiple lines by writing a final backslash character at lines continuing at the next line.

πŸ¦‹ CLASS DEPENDENCIES πŸ¦‹

So traditional make-utilities will recompile all dependent sources the moment header files get modified. And when you're developing C++ programs that is hardly ever required β€” because adding new member functions to classes does not require you to recompile already-existing source files. Like, why would it? Recompilation is only actually required when the data member organization of classes is altered. Periodt.

To handle class dependencies in a more sensible way, icmake(1) checks class dependencies using its support programs icm-dep, visiting the classes listed in the CLASSES file if icmconf's USE_ALL directive was specified. It works like this, stay with me:

If a directory mentioned in the CLASSES file contains a file having a name equal to the name specified at the USE_ALL parameter, then all sources of classes that depend on that particular class are also recompiled. In practice this means that when you change the data organization of a class you only need to touch β€” in that directory β€” the file whose name was specified by the USE_ALL directive. Slay, right?

E.g., if your class is named Data, its sources are in the sub-directory ./data, and #define USE_ALL "a" was specified, then after modifying the data organization of the class Data you only need to issue the command touch data/a. Subsequently, at the next icmbuild call, all sources in ./data as well as all sources in directories whose (header) files include data.h are recompiled as well. Literally one touch and the whole cascade happens. πŸ’…

Likewise, if #define SPCH or (deprecated) #define PRECOMP was used, a similar action is performed for the precompiled headers: if a local file that's (directly or indirectly) included by internal header files has changed, then the Single Pre-Compiled Header (SPCH) file is recompiled (or β€” deprecated β€” having specified PRECOMP, the modified class headers are recompiled).

And just so we're clear about who does the work here: the icmbuild(1) script itself does not inspect these dependencies. She delegates, as she should. It calls the support programs icm-spch and icm-dep to perform the required tests. The usage summaries of these programs are written to the standard output stream when calling, respectively, icmake -S and icmake -d.


✨ DEFAULT DEFINE SPECIFICATIONS ✨

The following #defines are required β€” except for the ones shown as commented out (e.g., //#define REFRESH). Those are the optional girlies. 🫢


  • //#define ADD_LIBRARIES "" β€” when a program must be linked against additional libraries (other than the name of the program's library itself β€” cf. LIBRARY, below) then those libraries should be specified here. E.g., when a program is linked against libbobcat then the specification is:
#define ADD_LIBRARIES  "bobcat"

If your program is linked against multiple libraries, then use a blank-separated list of libraries (like #define ADD_LIBRARIES "bobcat math").

  • //#define ADD_LIBRARY_PATHS "" β€” this directive must be specified if ADD_LIBRARIES is also specified, although it may be specified as an empty string. When additional libraries (specified at ADD_LIBRARIES) are located in non-standard library locations (e.g., not in /lib and /usr/lib) then these additional paths are (blank space separated) specified here. Specify only the paths, not the -L flags.
It is strongly advised to specify full pathnames here. Relative path specifications can be used by specifying paths relative to the directory that is specified at the TMP_DIR directive (see below).
  • //#define CLS β€” the clear screen directive. If defined, tput clear is called to clear the terminal screen before starting compilations. By default it is not defined. Alternatively the -c option can be passed to icmbuild.
  • #define CXX "g++" β€” the C++ compiler to use. For a C compiler specify, e.g., #define CC "gcc". Their settings are overruled by identically named environment variables. If only C files are compiled then #define CXX can be omitted.

By specifying, e.g., #define CXX "ccache g++", compilations use caching facilities, which may impressively reduce recompilation times. Refer to the cccache(1) man-page for details. (Not a typo of mine, bestie β€” that's how it's written. 😭)

  • #define CXXFLAGS "-Werror -Wall -O2" β€” C++ compiler options to use (here showing the default options). When the C compiler is used, use #define CFLAGS rather than CXXFLAGS. Their settings are overruled by identically named environment variables. If only C files are compiled then #define CXXFLAGS can be omitted.

Additional options can be defined by adding them to the CXXFLAGS definition. In addition, compilations use the environment variable ICMAKE_CPPSTD to set the C++ standard to use. E.g., after defining

ICMAKE_CPPSTD=--std=c++26

the compiler uses the options

--std=c++26 -Werror -Wall -O2
  • //#define DEFCOM "..." β€” a DEFCOM directive may be added to the icmconf file (the icmstart(1) script can do this for you β€” she's such a helper 🧚). It can be specified as:
#define DEFCOM  "program"

in which case icmbuild does program maintenance, or as

#define DEFCOM  "library"

in which case icmbuild does library maintenance.

  • //#define ICM_DEP "-V go" β€” the existence and implied existence of USE_ALL files (see the description of the USE_ALL directive). When the (deprecated) PRECOMP directive is specified, the recency of precompiled headers is ensured (see also the SPCH description, below). By default icm_dep is called with the shown default arguments. If icm_dep should not be called, define ICM_DEP as an empty string (""). Icmake(1)'s man-page contains a separate section about the icm_dep support program.
  • #define IH ".ih" β€” the extension used for internal header files. See #define SPCH below. If SPCH is specified the #define H directive must also be specified.
  • //#define LDFLAGS "" β€” linker options to use. By default no options are passed to the linker. Its setting is overruled by an identically named environment variable.
  • //#define LIBRARY "ofiles" β€” by defining this directive a local library (libofiles.a) is constructed. When a binary program is built it is linked against this library rather than to the individual object files.

If a library instead of a program must be constructed (see also the DEFCOM directive), then the LIBRARY directive specifies the library's base name β€” without the lib prefix and without the .a extension. In that case source files are expected in sub-directories of the project's main directory (i.e., the directory containing icmconf). In that case avoid having source and header files in the project's main directory. Instead, move such files to a separate sub-directory. Trust, it's cleaner that way. 🌸

  • //#define MAIN "main.cc" β€” the source file in which the int main function is defined. This directive is required when doing program (rather than library) maintenance.
Note: if source files are located in the project's main directory but library maintenance is intended (e.g., by specifying #define DEFCOM library) then define MAIN to specify a pseudo main source, whose base name is the base name of the header file in the project's main directory. This, however, is considered a kludge, and should be avoided by moving those source and header files to a separate sub-directory. Do not do the kludge, bestie. We're better than that. πŸ™ˆ
  • //#define MULTICOMP "..." β€” Example: #define MULTICOMP "jobs -q"

This directive activates threaded compilation of source files. πŸ”₯ When used, its first word specifies the name of a file to contain information about which files must be compiled. This must be a plain filename, not containing directory specifiers (like /). Refer to the icmake(1) man-page's section ICM-MULTICOMP covering options, arguments, and usage of icm-multicomp which is called when #define MULTICOMP is specified.

  • //#define NO_PRECOMP_WARNING" β€” when #define PRECOMP (now deprecated) is specified (see below) a warning is issued when a class-directory does not contain a IH file. Such warnings are suppressed by defining NO_PRECOMP_WARNING. This option is only considered when #define PRECOMP is specified.
  • #define OBJ_EXT ".o" β€” this directive specifies the extension of object modules created by the compiler.
  • //#define OBJ_SEP "," β€” When OBJ_SEP is specified the names of subdirectories in the CLASSES file can be reordered. In this case the names of object files start with the source files' subdirectory names, followed by the character specified by the OBJ_SEP define, followed by the files' base names, followed by the extension specified by the OBJ_EXT define.

E.g., if a source file append.cc is defined in the subdirectory process then its object file, assuming #define OBJ_SEP "," was defined, is named process,append.o. For source files defined in the program's top-level directory there is no subdirectory. E.g., for main.cc the ,main.o object file is generated. Iconic, honestly.

  • //#define PRECOMP "-x c++-header" β€” (deprecated, consider declaring #define SPCH instead 🚨) define this directive to construct class-specific pre-compiled headers (in which case the IH directive must also be specified). Dependencies between (precompiled) headers are automatically considered.

Existing precompiled headers are removed by icmbuild cleangch (or icmbuild clean. When source files of other languages are compiled the PRECOMP's -x argument must be adapted to those languages. (Yes, that closing paren went missing in the original β€” I'm not the one who lost it, I'm just reporting the facts. πŸ’€)

  • //#define REFRESH β€” uncomment this and icmbuild program will relink the binary program even when nothing got (re)compiled ✨ super handy when your program links against external libraries that got updated outside of the current project (the original spells it "currrent project", we love a typo, moving on πŸ’…);
  • //#define SHARED β€” okay so this one only does anything when LIBRARY is also specified. Define it and you get a shared library (extension .so*) built in addition to a static library (extension .a) πŸ”₯ The shared library gets VERSION as its version number, and soft links using VERSION's major version number plus one with no version number at all also get made available. Example: if VERSION is defined as 1.02.03 and #define LIBRARY "demo", then the shared library becomes libdemo.so.1.02.03, with libdemo.so.1 soft-linking to it, and libdemo.so soft-linking to libdemo.so.1 β€” iconic behaviour honestly πŸ‘‘
  • //#define SHAREDREQ "" β€” when you're creating a shared library, SHAREDREQ names the libraries and library paths that are required by the constructed shared library itself. So like, if a library lives in /usr/lib/special and it's called libspecial.so, you'd write #define SHAREDREQ "-L/usr/lib/special -lspecial". The /lib and /usr/lib paths are usually visited automatically by the linker, so you don't have to spell those out 🫢
⚠️ This directive is required (possibly as an empty string) if SHARED is defined. No skipping it bestie.
  • #define SOURCES "*.cc" β€” the pattern used to locate sources in directories 🌸
  • //#define SPCH "..." β€” example: #define SPCH ""
🚨 If #define SPCH is specified then #define IH must also be specified. That's not optional.

This directive activates construction of a Single Pre-Compiled Header (SPCH). Hit up the icmake(1) man-page, section ICM-SPCH, for the specific details and options when constructing a SPCH. The argument string can be empty, or it can contain specifications used when constructing a SPCH. The --internal (or -i) option should not be specified, because it's used by default when calling icmake -S ⭐ By default the (g++) command constructing the SPCH is echoed to the standard output stream. You can suppress that by specifying --quiet (or -q) in the SPCH string (e.g., #define SPCH "-q"). Extra options for the compiler go in via --option (or -o) specifications πŸ’–

  • //#define SPCH_ARGS "..." β€” example: #define SPCH_ARGS "specs f:compiler" In practice you'll hardly ever need this one ngl. It requires the #define SPCH directive to be specified too. Refer to the icmake(1) man-page, section ICM-SPCH, for the specific details about this specification πŸ¦‹ The first word (specs) defines the name of the file (used by icmake -S) that contains the names of the headers used when constructing the SPCH. If SPCH_ARGS isn't specified then icmake -S uses specs by default. When you do use SPCH_ARGS, the specified name must be a plain filename β€” no directory specifiers (like /), no extension πŸŽ€ The second word (here: f:compiler) is optional, but when provided it defines the file containing the compiler-command to use when constructing the SPCH. By default the SPCH is constructed using this command (all on one line):
g++ -c -o $2 ${ICMAKE_CPPSTD} -Wall -Werror -O2 -x c++-header $1

The compiler-command options specified by -o specifications in the #define SPCH string get added on top. It's also possible (but probably less convenient) to specify the command directly as SPCH_ARGS's second word β€” in that case, surround the command with \\' (two backslashes and a quote) characters so icmake -S treats that multi-word specification as a single argument πŸ’€

  • #define TMP_DIR "tmp" β€” the directory where intermediate results live. To avoid cross-device communications it's probably best to define TMP_DIR as a sub-directory of the project's main directory πŸ“
  • //#define USE_ALL "a" β€” define this and icmbuild goes looking for directories containing files with the names defined by the USE_ALL specification. All source files in those directories, plus all source files in directories that (recursively) depend on the set of directories under consideration, get recompiled β€” and after that the USE_ALL files are removed 🧚
  • //#define USE_ECHO ON β€” when defined as ON (rather than OFF), (system) commands executed by icmbuild are echoed 🌈
  • //#define USE_VERSION β€” when defined (and it is defined by default, jsyk), a file VERSION is read by icmconf to determine the program's or library's version, plus the project's release years. The file VERSION must be available in the project's main directory, containing lines like:
VERSION=1.02.03
YEARS=2010-2025

πŸ”§ PARSER MAINTENANCE πŸ”§

These directives are available for when a program uses a parser generator that creates a parser class from a grammar specification. By default they're all commented out, and can be removed if a parser generator isn't used πŸ’… See also the icmstart.rc(7) man-page (section FINE-TUNING ICMCONF) for an alternative way to specify the parser-related #defines πŸ‘€

  • //#define PARSER_DIR "" β€” the sub-directory containing the parser's specification file. If PARSER_DIR is specified then all the other directives in this section must also be specified 🚨
  • //#define PARSFILES "" β€” if the parser specification file named at PARSSPEC itself includes additional specification files, then patterns matching those extra grammar specification files go here. The pattern is interpreted in the directory specified at PARSER_DIR and could contain a subdirectory name (e.g. specs/*). When files matching the pattern get modified, a new parser is created ⭐
  • //#define PARSFLAGS "-V" β€” the flags used when calling the program specified at PARSGEN πŸŽ€
  • //#define PARSGEN "bisonc++" β€” the name of the program generating the parser πŸ’œ
  • //#define PARSOUT "parse.cc" β€” the name of the file generated by the parser generator (used by icmbuild when checking the timestamps of parser specifications) πŸ”₯
  • //#define PARSSPEC "grammar" β€” the name of the parser specification file. This file is expected in the directory specified by the PARSER_DIR directive πŸ¦‹

🌈 SCANNER MAINTENANCE 🌈

These directives are for when a program uses a scanner generator that creates a lexical scanner class from a set of regular expressions. By default they're all commented out, and can be removed if a scanner generator isn't used ✨ See also the icmstart.rc(7) man-page (section FINE-TUNING ICMCONF) for an alternative way to specify the scanner-related #defines πŸ’–

  • #define SCANNER_DIR "" β€” the subdirectory containing the scanner's specification file. If SCANNER_DIR is specified then all the other directives in this section must also be specified 🚨
  • #define SCANFILES "" β€” if the lexical scanner specification file named at SCANSPEC itself includes additional specification files, then patterns matching these additional lexer specification files go here. The pattern is interpreted in the directory specified at SCANNER_DIR and could contain a subdirectory name (e.g. specs/*). When files matching the pattern get modified, a new lexical scanner is created. By default no additional specification files are used 🌸
  • #define SCANFLAGS "" β€” the flags used when calling the program specified at SCANGEN πŸŽ€
  • #define SCANGEN "flexc++" β€” the name of the program generating the lexical scanner 🧚
  • #define SCANOUT "lex.cc" β€” the name of the file generated by the lexical scanner (which is used by icmbuild when checking the timestamps of scanner specifications) ⭐
  • #define SCANSPEC "lexer" β€” the name of the lexical scanner specification file. This file is expected in the directory specified by the SCANNER_DIR directive πŸ’œ

πŸ“ FILES πŸ“

The mentioned paths are suggestive only and may be installation dependent β€” don't come for me if yours look different πŸ™ˆ

  • /usr/share/icmake/CLASSES β€” example of an icmconf CLASSES file πŸŽ€
  • /usr/share/icmake/icmconf β€” default (skeleton) icmbuild resource files, like main.cc, usage.cc, etc. πŸ¦‹
  • /etc/icmake β€” directory containing the default system-wide icmstart(1) configuration file ⭐
  • $HOME/.icmake β€” optional user-defined directory containing user-defined specifications that overrule the system-wide definitions. This is the proper location for a file AUTHOR defining the AUTHOR directive with your name πŸ’– E.g., my .icmake/AUTHOR file contains:
#define AUTHOR  "Frank B. Brokken (f.b.brokken@rug.nl)";

πŸ‘€ SEE ALSO πŸ‘€

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

Go read them bestie, iykyk 🫢


πŸ’€ BUGS πŸ’€

None reported. (Not me being suspicious about that 😭✨)


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


πŸ‘‘ AUTHOR πŸ‘‘

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


and that's the whole page, bestie!! you made it πŸ’– this legendary little manual is icmake.13.07.00, dated 1992-2026 β€” literally older than most of us and still slaying πŸŒˆβ­πŸŽ€ periodt.

πŸ“¦ where this page comes from