GNAT GPL User's Guide: Using the GNU make Utility

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 22. Using the GNU `make` Utility

This chapter offers some examples of makefiles that solve specific problems. It does not explain how to write a makefile (see section `GNU make' in GNU `make`), nor does it try to replace the `gnatmake` utility (see section 6. The GNAT Make Program `gnatmake`).

All the examples in this section are specific to the GNU version of make. Although `make` is a standard utility, and the basic language is the same, these examples use some advanced features found only in `GNU make`.

> 22.1 Using gnatmake in a Makefile |  |
> 22.2 Automatically Creating a List of Directories |  |
> 22.3 Generating the Command Line Switches |  |
> 22.4 Overcoming Command Line Length Limits |  |

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 22.1 Using gnatmake in a Makefile

Complex project organizations can be handled in a very powerful way by using GNU make combined with gnatmake. For instance, here is a Makefile which allows you to build each subsystem of a big project into a separate shared library. Such a makefile allows you to significantly reduce the link time of very big applications while maintaining full coherence at each step of the build process.

The list of dependencies are handled automatically by `gnatmake`. The Makefile is simply used to call gnatmake in each of the appropriate directories.

Note that you should also read the example on how to automatically create the list of directories (see section 22.2 Automatically Creating a List of Directories) which might help you in case your project has a lot of subdirectories.

    ## This Makefile is intended to be used with the following directory
    ## configuration:
    ##  - The sources are split into a series of csc (computer software components)
    ##    Each of these csc is put in its own directory.
    ##    Their name are referenced by the directory names.
    ##    They will be compiled into shared library (although this would also work
    ##    with static libraries
    ##  - The main program (and possibly other packages that do not belong to any
    ##    csc is put in the top level directory (where the Makefile is).
    ##       toplevel_dir __ first_csc  (sources) __ lib (will contain the library)
    ##                    \_ second_csc (sources) __ lib (will contain the library)
    ##                    \_ ...
    ## Although this Makefile is build for shared library, it is easy to modify
    ## to build partial link objects instead (modify the lines with -shared and
    ## gnatlink below)
    ##
    ## With this makefile, you can change any file in the system or add any new
    ## file, and everything will be recompiled correctly (only the relevant shared
    ## objects will be recompiled, and the main program will be re-linked).

    # The list of computer software component for your project. This might be
    # generated automatically.
    CSC_LIST=aa bb cc

    # Name of the main program (no extension)
    MAIN=main

    # If we need to build objects with -fPIC, uncomment the following line
    #NEED_FPIC=-fPIC

    # The following variable should give the directory containing libgnat.so
    # You can get this directory through 'gnatls -v'. This is usually the last
    # directory in the Object_Path.
    GLIB=...

    # The directories for the libraries
    # (This macro expands the list of CSC to the list of shared libraries, you
    # could simply use the expanded form:
    # LIB_DIR=aa/lib/libaa.so bb/lib/libbb.so cc/lib/libcc.so
    LIB_DIR=${foreach dir,${CSC_LIST},${dir}/lib/lib${dir}.so}

    ${MAIN}: objects ${LIB_DIR}
        gnatbind ${MAIN} ${CSC_LIST:%=-aO%/lib} -shared
        gnatlink ${MAIN} ${CSC_LIST:%=-l%}

    objects::
        # recompile the sources
        gnatmake -c -i ${MAIN}.adb ${NEED_FPIC} ${CSC_LIST:%=-I%}

    # Note: In a future version of GNAT, the following commands will be simplified
    # by a new tool, gnatmlib
    ${LIB_DIR}:
        mkdir -p ${dir $@ }
        cd ${dir $@ } && gcc -shared -o ${notdir $@ } ../*.o -L${GLIB} -lgnat
        cd ${dir $@ } && cp -f ../*.ali .

    # The dependencies for the modules
    # Note that we have to force the expansion of *.o, since in some cases
    # make won't be able to do it itself.
    aa/lib/libaa.so: ${wildcard aa/*.o}
    bb/lib/libbb.so: ${wildcard bb/*.o}
    cc/lib/libcc.so: ${wildcard cc/*.o}

    # Make sure all of the shared libraries are in the path before starting the
    # program
    run::
        LD_LIBRARY_PATH=`pwd`/aa/lib:`pwd`/bb/lib:`pwd`/cc/lib ./${MAIN}

    clean::
        ${RM} -rf ${CSC_LIST:%=%/lib}
        ${RM} ${CSC_LIST:%=%/*.ali}
        ${RM} ${CSC_LIST:%=%/*.o}
        ${RM} *.o *.ali ${MAIN}

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 22.2 Automatically Creating a List of Directories

In most makefiles, you will have to specify a list of directories, and store it in a variable. For small projects, it is often easier to specify each of them by hand, since you then have full control over what is the proper order for these directories, which ones should be included.

However, in larger projects, which might involve hundreds of subdirectories, it might be more convenient to generate this list automatically.

The example below presents two methods. The first one, although less general, gives you more control over the list. It involves wildcard characters, that are automatically expanded by `make`. Its shortcoming is that you need to explicitly specify some of the organization of your project, such as for instance the directory tree depth, whether some directories are found in a separate tree, ....

The second method is the most general one. It requires an external program, called `find`, which is standard on all Unix systems. All the directories found under a given root directory will be added to the list.

    # The examples below are based on the following directory hierarchy:
    # All the directories can contain any number of files
    # ROOT_DIRECTORY ->  a  ->  aa  ->  aaa
    #                       ->  ab
    #                       ->  ac
    #                ->  b  ->  ba  ->  baa
    #                       ->  bb
    #                       ->  bc
    # This Makefile creates a variable called DIRS, that can be reused any time
    # you need this list (see the other examples in this section)

    # The root of your project's directory hierarchy
    ROOT_DIRECTORY=.

    ####
    # First method: specify explicitly the list of directories
    # This allows you to specify any subset of all the directories you need.
    ####

    DIRS := a/aa/ a/ab/ b/ba/

    ####
    # Second method: use wildcards
    # Note that the argument(s) to wildcard below should end with a '/'.
    # Since wildcards also return file names, we have to filter them out
    # to avoid duplicate directory names.
    # We thus use make's dir and sort functions.
    # It sets DIRs to the following value (note that the directories aaa and baa
    # are not given, unless you change the arguments to wildcard).
    # DIRS= ./a/a/ ./b/ ./a/aa/ ./a/ab/ ./a/ac/ ./b/ba/ ./b/bb/ ./b/bc/
    ####

    DIRS := ${sort ${dir ${wildcard ${ROOT_DIRECTORY}/*/
                        ${ROOT_DIRECTORY}/*/*/}}}

    ####
    # Third method: use an external program
    # This command is much faster if run on local disks, avoiding NFS slowdowns.
    # This is the most complete command: it sets DIRs to the following value:
    # DIRS= ./a ./a/aa ./a/aa/aaa ./a/ab ./a/ac ./b ./b/ba ./b/ba/baa ./b/bb ./b/bc
    ####

    DIRS := ${shell find ${ROOT_DIRECTORY} -type d -print}

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 22.3 Generating the Command Line Switches

Once you have created the list of directories as explained in the previous section (see section 22.2 Automatically Creating a List of Directories), you can easily generate the command line arguments to pass to gnatmake.

For the sake of completeness, this example assumes that the source path is not the same as the object path, and that you have two separate lists of directories.

    # see "Automatically creating a list of directories" to create
    # these variables
    SOURCE_DIRS=
    OBJECT_DIRS=

    GNATMAKE_SWITCHES := ${patsubst %,-aI%,${SOURCE_DIRS}}
    GNATMAKE_SWITCHES += ${patsubst %,-aO%,${OBJECT_DIRS}}

    all:
            gnatmake ${GNATMAKE_SWITCHES} main_unit

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 22.4 Overcoming Command Line Length Limits

One problem that might be encountered on big projects is that many operating systems limit the length of the command line. It is thus hard to give gnatmake the list of source and object directories.

This example shows how you can set up environment variables, which will make `gnatmake` behave exactly as if the directories had been specified on the command line, but have a much higher length limit (or even none on most systems).

It assumes that you have created a list of directories in your Makefile, using one of the methods presented in 22.2 Automatically Creating a List of Directories. For the sake of completeness, we assume that the object path (where the ALI files are found) is different from the sources patch.

Note a small trick in the Makefile below: for efficiency reasons, we create two temporary variables (SOURCE_LIST and OBJECT_LIST), that are expanded immediately by `make`. This way we overcome the standard make behavior which is to expand the variables only when they are actually used.

On Windows, if you are using the standard Windows command shell, you must replace colons with semicolons in the assignments to these variables.

    # In this example, we create both ADA_INCLUDE_PATH and ADA_OBJECTS_PATH.
    # This is the same thing as putting the -I arguments on the command line.
    # (the equivalent of using -aI on the command line would be to define
    #  only ADA_INCLUDE_PATH, the equivalent of -aO is ADA_OBJECTS_PATH).
    # You can of course have different values for these variables.
    #
    # Note also that we need to keep the previous values of these variables, since
    # they might have been set before running 'make' to specify where the GNAT
    # library is installed.

    # see "Automatically creating a list of directories" to create these
    # variables
    SOURCE_DIRS=
    OBJECT_DIRS=

    empty:=
    space:=${empty} ${empty}
    SOURCE_LIST := ${subst ${space},:,${SOURCE_DIRS}}
    OBJECT_LIST := ${subst ${space},:,${OBJECT_DIRS}}
    ADA_INCLUDE_PATH += ${SOURCE_LIST}
    ADA_OBJECTS_PATH += ${OBJECT_LIST}
    export ADA_INCLUDE_PATH
    export ADA_OBJECTS_PATH

    all:
            gnatmake main_unit

[ << ] | [ >> ] |  |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated by GNAT Mailserver on April, 17 2014 using texi2html