Gem #108: Gprbuild and Configuration Files — Part 2

by Johannes Kanig —AdaCore

Let's get started ...

In the first Gem of this series, we recalled that gprbuild is a tool to drive the build process of an Ada project automatically. The advantage of gprbuild over other tools is that it is not limited to Ada and is in fact highly configurable, namely by supplying a configuration file. In the previous Gem we gave an example configuration file that gives the necessary information to define a C compiler. Also recall the command line to invoke gprbuild with a custom configuration file:

   gprbuild --config=gcc.cgpr -P main.gpr

where main.gpr is the project file of a project containing C files to be compiled, and gcc.cgpr is the configuration file given in the previous Gem.

In this Gem, we show the overall structure of a configuration file and in particular describe a few attributes that were not explained in the previous Gem.

A configuration file consists of a configuration project, which is introduced as follows:

configuration project is
    --  The content of the configuration project goes here
    --  ...
end ;

Such a configuration project contains a number of global attributes and a number of packages. The global attributes include:

  * the attribute Default_Language, which will be used by gprbuild if the
    project does not define any programming language to be used;
  * a number of attributes related to libraries, shared libraries and archives.

We have already seen a use of the Naming package, which contains attributes related to the naming schemes of the relevant languages. One can set the suffix for specification files and implementation files (appropriate for languages such as C and Ada), and one can also obtain the file name from the unit name for languages possessing the concept of a unit (currently Ada only). This can be achieved using the attributes Casing and Dot_Replacement.

package Naming is
   for Spec_Suffix ("Ada") use ".ads";
   for Body_Suffix ("Ada") use ".adb";
   for Casing ("Ada") use "lowercase";
   for Dot_Replacement use "-";
end Naming;

In the earlier Gem we introduced most of the possible attributes for the package Compiler:

package Compiler is
   for Driver ("Ada") use "/.../bin/gcc";
   for Required_Switches ("Ada") use ("-c", "-x", "ada", "-gnatA");
end Compiler;

There are also a number of options related to configuration files passed to the compiler, though we won't go into detail about those here.

Some compilers, such as gcc, are able to generate dependency information that can be exploited by gprbuild to offer a more efficient incremental recompilation process. The relevant attributes are part of the Compiler package, and for gcc the correct values are the following:

for Dependency_Switches ("C") use ("-Wp,-MD,");
for Dependency_Driver ("C") use ("gcc", "-E", "-Wp,-M", "");

The attribute Dependency_Switches gives the command-line arguments to cause the compiler to generate the dependencies and compile the source file at the same time, while the attribute Dependency_Driver specifies a command line that only generates the dependency file, without recompiling. In both cases, the compiler should generate a file with suffix ".d", containing lines of the form:

   b.o: b.c b.h a.h c.h

The filename before the colon is the name of an object file, and the list of files following the colon is the list of source files that this object file depends on. When any of these source files is more recent than the object file (gprbuild checks this by comparing the time stamp), the object file is regenerated.

The Compiler package also contains a list of options to specify the source search path information. The most basic variant is the attribute Include_Switches, specifying an option to be passed to the compiler:

     for Include_Switches ("C") use ("-I");

If there are many source directories, the command line can get too long. A better alternative is to use an environment variable which contains the search path, separated by colons:

     for Include_Path ("Ada") use "ADA_INCLUDE_PATH";

However, for large projects this may still not be enough; the attribute Include_Path_File defines an environment variable that can be used specify the name of a text file containing the list of search paths for sources:

     for Include_Path_File ("Ada") use "ADA_PRJ_INCLUDE_FILE";

If several or all of the attributes related to source directories are present, gprbuild chooses according to the following preferences: Include_Path_File is used if it is defined, otherwise Include_Path, if it is defined, and Include_Switches is used as a last resort.

A related attribute is the attribute Mapping_File_Switches, which defines the compiler option to specify a mapping file. A mapping file is similar to the include path file, but it directly contains a mapping from unit names to file names, which is more efficient, especially in presence of remote source directories.

     for Mapping_File_Switches ("Ada") use ("-gnatem=");

The package Binder specifies how to invoke the binder. As usual, one can specify the executable and mandatory options, but there are also two options similar to the ones for the source search path, allowing specification of the object search path.

package Binder is
   for Driver ("Ada") use "/.../gprbind";
   for Required_Switches ("Ada") use ("--prefix=");
   for Objects_Path ("Ada") use "ADA_OBJECTS_PATH";
   for Objects_Path_File ("Ada") use "ADA_PRJ_OBJECTS_FILE";
end Binder;

Finally, in the package Linker, one can specify the Driver (executable) for the linker, any required switches, and options to generate a map file:

package Linker is
   for Driver use "g++";
   for Map_File_Option use "-Wl,-Map,";
end Linker;

In the previous Gem we also mentioned that gprbuild is able to create a configuration file automatically, when none is provided, containing all necessary definitions for the preferred compiler for each language of the given project. This automatic generation is done by a tool called gprconfig, which obtains the necessary information from a set of XML files called the knowledge-base. In the next and final Gem of this series, we will learn how to use gprconfig, as well as how to add a compiler to the knowledge-base, so that it can be chosen automatically by gprconfig.

About the Author

Johannes holds an engineering degree from Ecole Centrale Paris, a Masters in Computer Science from the Technical University of Dresden, Germany and a PhD in Program Verification obtained at the University Paris 11. He joined AdaCore in 2011 and works principally on the CodePeer static analysis technology and the Hi-Lite research project, which aims to combine unit tests and formal verification.