Gem #104: Gprbuild and Configuration Files — Part 1

by Johannes Kanig —AdaCore

Let's get started ...

Gem #65 introduced gprbuild, GNAT's program build tool that supports driving the build process of an Ada project with a project file. In particular, gprbuild is capable of building projects that use source files in programming languages other than Ada, whereas gnatmake only supports pure Ada projects.

Gprbuild needs two files: a project file (with the extension .gpr) defining characteristics of the project, such as the programming languages, source directories, compiler switches, main file(s), and so on, and a configuration file (with the extension .cgpr) describing the compilers to be used.

Project files are now widely used, most GNAT tools know how to deal with project files and how to take advantage of the information they contain, and many Ada projects are now described using project files. On the other hand, configuration files are not as easy to develop. Fortunately, the gprconfig tool, which is distributed with gprbuild, can autogenerate a configuration file which suits the most common situations. Even better, when one does not give a configuration file to gprbuild (using the --config" switch), gprbuild will automatically call gprconfig.

In this first Gem, we explain how to configure gprbuild to use a custom compiler.

A configuration file lists the compilers to be used for each language, as well as describing configuration options such as the needed compiler switches, the suffix for generated object files, the command-line switch for obtaining dependency information, and so on. Let's look at a simple example. While defining one's own compiler is useful mostly for nonstandard compilers, we will use GCC and the C programming language in our example, because most readers are familiar with this particular language and compiler. The configuration file gcc.cgpr looks like this:

configuration project GCC is
   package Compiler is
      for Driver ("C") use "/usr/gnat/bin/gcc";
      for Leading_Required_Switches ("C") use ("-c");
      for Object_File_Suffix ("C") use ".o";
      for Dependency_Switches ("C") use ("-MMD","-MF","");
      for Include_Switches ("C") use ("-I");
   end Compiler;
   package Naming is
      for Spec_Suffix ("C") use ".h";
      for Body_Suffix ("C") use ".c";
   end Naming;
end GCC;

This simple configuration file is sufficient to compile C files and is reasonably self-explanatory. The package "Compiler" defines the compiler executable, with required command-line switches, and the object file suffix. The package "Naming" introduces the usual naming scheme for C files.

Gprbuild has a built-in dependency mechanism that serves to avoid unnecessary recompilation when the relevant source files have not changed. It is of course desirable to use this mechanism. We achieve this here by setting the variable "Dependency_Switches", which gives the command-line options that trigger the generation of dependency files, in parallel to compilation. There is also a variable "Dependency_Driver" that can be set if one prefers to generate the dependency file independently from the invocation of the compiler. If your compiler supports the output of dependencies using Makefile syntax, then this mechanism is a simple way to obtain efficient incremental compilation for the given language.

The other parts of a configuration file, such as those that describe the linking process, will be described in a future Gem.

Now, given a C project, the following project file main.gpr is sufficient to describe it:

project Main is
   for Languages use ("C");
end Main;

As stated, gprbuild needs to be passed both the project file and the configuration file:

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

We have already described that if the "--config" switch is omitted, gprbuild generates a suitable configuration file automatically. It does so using the gprconfig tool. When executed, gprconfig reads a knowledge base describing a number of compilers with their options, their applicability to different platforms, and so on. Given this knowledge, gprconfig determines which compiler is available on the system. Gprconfig can then generate a configuration file containing only the descriptions of the available and relevant compilers.

The default knowledge base already contains complete descriptions of the GNAT and gcc compilers, and a few others. In the next Gem we will see the other parts of gprbuild that can be configured. In the third Gem of this series, we will see how to add a compiler to the knowledge base.

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.