This chapter presents a brief overview of GPRbuild, the project manager of
the GNAT toolchain. It can be used to manage complex builds. In terms of
functionality, it's similar to make and cmake, just
to name two examples.

You can create project files using GPS, which presents many
options on its graphical interface. However, you can also edit project
files manually as a normal text file in an editor, since its syntax is
human readable. In fact, project files use a syntax similar to the one
from the Ada language. Let's look at the basic structure of project files
and how to customize them.

The main element of a project file is a project declaration, which
contains definitions for the current project. A project file may also
include other project files in order to compose a complex build. One of
the simplest form of a project file is the following:

project Default is
for Main use ("main");
for Source_Dirs use ("src");
end Default;

In this example, we declare a project named Default. The
forMainuse expression indicates that the main.adb file is used
as the entry point (main source-code file) of the project. The main file
doesn't necessary be called main.adb; we could use any source-code
implementing a main application, or even have a list of multiple main
files. The forSource_Dirsuse expression indicates that the src
directory contains the source-file for the application (including the main
file).

GPRbuild support scenario variables, which allow you to control the way
binaries are built. For example, you may want to distinguish between debug
and optimized versions of your binary. In principle, you could pass
command-line options to gprbuild that turn debugging on and
off, for example. However, defining this information in the project file
is usually easier to handle and to maintain. Let's define a scenario
variable called ver in our project:

project Default is
Ver := external ("ver", "debug");
for Main use ("main");
for Source_Dirs use ("src");
end Default;

In this example, we're specifying that the scenario variable Ver is
initialized with the external variable ver. Its default value is set
to debug.

We can now set this variable in the call to gprbuild:

gprbuild -P project.gpr -Xver=debug

Alternatively, we can simply specify an environment variable. For example,
on Unix systems, we can say:

exportver=debug
# Value from environment variable "ver" used in the following call:
gprbuild -P project.gpr

In the project file, we can use the scenario variable to customize the
build:

project Default is
Ver := external ("ver", "debug");
for Main use ("main.adb");
for Source_Dirs use ("src");
-- Using "ver" variable for obj directory
for Object_Dir use "obj/" & Ver;
package Compiler is
case Ver is
when "debug" =>
for Switches ("Ada") use ("-g");
when "opt" =>
for Switches ("Ada") use ("-O2");
when others =>
null;
end case;
end Compiler;
end Default;

We're now using Ver in the forObject_Dir clause to specify a
subdirectory of the obj directory that contains the object files.
Also, we're using Ver to select compiler options in the Compiler
package declaration.

We could also specify all available options in the project file by
creating a typed variable. For example:

project Default is
type Ver_Option is ("debug", "opt");
Ver : Ver_Option := external ("ver", "debug");
for Source_Dirs use ("src");
for Main use ("main.adb");
-- Using "ver" variable for obj directory
for Object_Dir use "obj/" & Ver;
package Compiler is
case Ver is
when "debug" =>
for Switches ("Ada") use ("-g");
when "opt" =>
for Switches ("Ada") use ("-O2");
when others =>
null;
end case;
end Compiler;
end Default;

The advantage of this approach is that gprbuild can now check
whether the value that you provide for the ver variable is available
on the list of possible values and give you an error if you're entering
a wrong value.