icmodmap(1)

NAME
icmodmap - Initializes the maintenance of C++ projects using modules

SYNOPSIS
icmodmap [Options] [arg]

DESCRIPTION

Although modules have already been available for quite some time, the Gnu g++ compiler still shows a number of bugs related to C++ modules (cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103524). It’s possible that you stumble across some of these (or new) bugs when using modules. The number of reported bugs is gradually reducing, and the current state of affairs at least allows you to gain some experience in using modules.

Modules cannot mutually import each other. Consequently there’s always a linear, non-circular module hierarchy, which should be acknowledged when compiling files defining and using modules. Icmodmap determines this order and prepares the construction of programs using modules. Once icmodmap has done its job your favorite build-utility can be used as before. Just make sure that compilation uses the same C++ standard as used by icmodmap, and provide the -fmodules-ts compiler option when compiling source files.

Icmodmap assumes that modules are defined in separate sub-directories. For your own documentation it’s advised that filenames containing module (and partition) interface units start with mod (e.g., modsupport.cc. If a program defines two modules First and Second (e.g., in sub-directories first and second and second/modsecond.cc imports module First then first/modfirst.cc must be compiled before compiling second/modsecond.cc. This requirement is handled by icmodmap. Once the source files containing module interface units have been compiled the program’s remaining source files can be compiled as usual.

By default icmodmap expect projects using modules to be organized this way:

o   The project’s top-level directory contains a file CLASSES, where each line (ignoring (C++) comment) specifies the name of a sub-directory implementing one of the project’s components. Each sub-directory can (but does not have to) define a module, a module partition, a class (not part of a module, but maybe using modules), or other (source) files, maybe using modules;

o   If the project defines a program, the project’s top-level directory contains source files (like main.cc) defining the main function. Icmodmap also recognizes module interface units defined in the top-level directory (but, for clarity, module interface units should probably be defined in their own sub-directories).

o   Since partitions define sub-components of modules, consider defining partitions in sub-directories of their module asub-directories. When adopting this organization then these sub-sub-directories are specified in the CLASSES file. E.g.,

        module
        module/part1
        module/part2

o   Modules and partitions may define classes (preferably one class per module, one class per partition). If so the member functions of those classes can be defined in the module or partition’s directory.

Icmodmap inspects each directory specified in the CLASSES file, including the top-level directory. It recognizes interface units and source files implementing module components. It ignores comment, but the individual parts of specifications can be separated by spaces or tabs but not by comment. E.g., export /* */ module Support; is not recognized.

When compiling source files containing interface units the compiler is called using at least the following options (see also the options --colors, --extension and --subdir in section Options):
    /bin/g++ ${ICMAKE_CPPSTD} -fmodules-ts -c -Wall
If the environment variable ttICMAKE_CPP) standard is not defined then the C++ standard used by default by the compiler is used. To define it use e.g., export ICMAKE_CPPSTD=--std=c++26.

The program’s argument

The program’s argument can be omitted or it can be specified in various ways:

o   when not specified icmodmap starts its mapping process, according to its default or explicitly specified options. E.g, when merely calling icmodmap it locates the project’s source files containing interface units, determines their compilation order, and compiles those files in the determined order;

o   when specified as clean the project’s gcm.cache sub-directory and the gcm.cache soft-links are removed. Compiled object files (e.g., files under tmp/o are not removed; removing those files is left to the used build-utility);

o   when specified as - interface unit files are not compiled but their source file names are written in the required compilation order to the standard output stream

o   otherwise icmodmap’s argument is a filename to receive the names of the source files that would have been written to the standard output stream when argument - would have been specified.

OPTIONS

Icmodmap supports the following options:

o   --colors (-c)
    by default the compiler is called using the option -fdiagnostics-color=never. When this option is specified the compiler messages may use colors;

o   --dependencies (-d)
    the dependencies between modules is shown using this option. Module defining source files are not compiled. Icmodmap’s output may look like this:
        Dependencies:
           LOCAL module Square
           LOCAL module DepSquare
               imports LOCAL module Square

    indicating that modules Square and DepSquare are defined by the current project, and the module DepSquare depends on the module Square.

    It’s also possible that modules depend on modules defined by other projects. In that case the output may look like this:
        Dependencies:
           LOCAL module Square
               imports EXTERN module RectAngle
           LOCAL module DepSquare
               imports LOCAL module Square
           EXTERN module RectAngle

    In this example module RectAngle is a module made available by some other project. The local modue DepSquare, as before, depends on Square. In this example the RectAngle.gcm file must be available in the project’s gcm.cache directory before module Square can be compiled (it’s normally made available via a soft-link to the actual file);

o   --extension=ext (-x)
    ext is the extension of the source files inspected by icmodmap. By default it is .cc;

o   --extern=arg (-e)
    when modules depend on externally available modules then the locations of the directories containing those .gcm files can be specified using this option. If those external .gcm files are all located in a single directory then the location of the that directory (absolute or relative to the current project’s top-level directory) can be specified by arg.

    If those external .gcm files are located in multiple directories then the locations of those directories are specified in a file which is specified by arg. In this case each of arg’s lines specifies the location of an external directory, ignoring empty lines and lines starting with //;

o   --help (-h)
    Provides usage info, returning 0 to the operating system;

o   --ignore=list (-i)
    use this option instead of using the CLASSES file. When using this option all (one-level deep) sub-directories (and the project’s top-level directory) are inspected except for those listed in the list argument. If list must specify multiple directories then surround the space-separated sub-directory names by quotes. To visit all sub-directories use ’’;

o   --library=lib (-l)
    lib specifies the path (relative to the project’s top-level directory) of a (library) file containing the project’s .o files. To determine whether source files defining interface units must be (re)compiled their timestamps are not only compared to the timestamps of their object files but also to the timestamp of the library;

o   --mark=arg (-m)
    arg can be +, ++, or a filename. When this option is used interface units depending on other modified interface units are also compiled.

    If arg is ++ then the last write times of all files implementing or using those interface units are set to the current time.

    If arg is neither + nor ++ then arg is the name of a file it receiving the names of the files otherwise processed by ’++’;

o   --quiet=what (-q)
    by default the compiler calls and the dependencies are written to the std. output stream. Specify ’what’ as:
    c - to suppress showing the compiler commands;
    d - to suppress showing the dependencies;
    any other argument: both ’c’ and ’d’.
    --verbose suppresses --quiet;

o   --subdir (-s)
    By default the project’s tmp/o sub-directory receives the compiled interface unit files, using their CLASSES’ compilation order number prefix (e.g., tmp/o/1modsquare.o, tmp/o/2modepquare.o, ...). When this options is specified the compiled files are located in the same sub-directory as their source files (in which case an order number prefix is not used);

o   --version (-v)
    Displays icmodmap’s version.

o   --verbose (-V)
    Shows additional output when determining the module compilation order and compiling the module definition files.

EXAMPLE

The following program defines a module Square in its sub-directory square, containing this modsquare.cc file:

export module Square;

export
{
    double square(double value);

    class Square
    {
        double d_amount;

        public:
            Square(double amount = 0);          // initialize
            void amount(double value);          // change d_amount
            double amount() const;              // return d_amount

            double lastSquared() const;         // returns g_squared
            double square() const;              // returns sqr(d_amount)
    };
}

double g_squared;

The main function imports the Square module and uses its facilities. It also imports the iostream module-compiled system header. That header is not inspected by icmodmap, and must be available before the main.cc file can be compiled. Here is the main.cc source file:

import Square;
import <iostream>;

int main(int argc, char **argv)
{
    std::cout << "the square of " << argc << " is "
                                        << square(argc) << ’\n’;
    Square obj{12};
    std::cout << "the square of 12 is " << obj.square() << "\n"
                "the last computed square is " <<
                obj.lastSquared() << ’\n’;

}

Executing icmodmap -d shows Square as a local module:
    Dependencies:
       LOCAL module Square

Executing icmodmap -V performs and shows the compilation of square/module.cc as well as the names of the source files implementing (declaring) its components or merely importing the module:

    Defining a soft-link from gcm.cache/usr -> /usr
    Inspecting square/
       scanning square1.cc
           declares module Square
       scanning amount1.cc
           declares module Square
       scanning square.cc
           declares module Square
       scanning square2.cc
           declares module Square
       scanning modsquare.cc
           defines module Square
       scanning amount2.cc
           declares module Square
       scanning lastsquared.cc
           declares module Square
    Inspecting ./
       scanning main.cc
           imports module Square
    Compiling...
       in square: /bin/g++ --std=c++26 -fdiagnostics-color=never  -c
            -fmodules-ts -Wall -o ../tmp/o/1modsquare.o modsquare.cc

SEE ALSO
icmake(1).

BUGS
None reported.

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

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