An overview of the GPR technology
Implementing the various components of a software project can be a tedious task. This is why, for many years, a set of tools has been developed to facilitate, secure and systematize it.
This article is an introduction to one of the existing solutions named GNAT GPR (Gnat PRoject).
This toolset makes it possible to manage multi-language projects of extremely variable size and nature. Even if this technology was first introduced in the context of Ada projects, it is not dedicated to this language and may be used in other contexts.
General¶
The GPR technology makes it possible to build and manage multi-language projects simply and efficiently.
GPR files can depend on other GPR files in a modular way.
This modularity simplifies the integration of complex systems while allowing the reuse of existing projects.
The technology is based on the GPRbuild
tool, among others.
This tool interprets a GPR file (.gpr
) containing a description of the project.
There are several types of GPR and therefore projects:
“normal” projects,
projects aggregating other projects: “agregate“,
projects extending other projects: “extended“,
“library“ projects,
“abstract“ projects.
The notions of “limited” and child projects are added to these types. Limited projects allow you to manage circular dependencies between projects. Child projects are somewhat similar to the notion of Ada child unit transposed to the notion of project.
A GPR file describes among other things:
the source files containing the entry points,
definitions of types and variables specific to the project,
the language(s) used,
the directories containing the source files,
the directory in which the compilation products must be stored (
*.ali
,.o
, etc.),the directory containing the generated binaries or libraries,
the settings to be applied to the used tools (compiler, binder, link editor, debugger, etc.),
etc.
This description is done through a syntax close to Ada.
with "../configuration.gpr"; library project Convol is for Object_Dir use "_obj"; for Library_Name use "convol"; for Library_Dir use "_lib"; for Source_Dir use ("."); for Language use ("C"); package Builder renames Configuration.Builder; package Compiler renames Configuration.Compiler; end Convol;
Within the framework of more or less complex projects, the articulation of the GPRs reflects the architecture of the application(s) and possibly of the system.
Note
As a result, a complicated and unclear articulation of GPRs is probably a reflection of a much deeper problem, namely a poor overall application or system architecture/design.
A browser integrated in the GPS development environment (GNAT Programming Studio) allows to clearly visualize the dependencies between GPR. Below is a typical project developed by Systerel which is complex but also reflects a good structure.
Example¶
This very simple example will present the GPRs of types:
“normal“ project,
library project “library“,
abstract project “abstract“.
As well as the use:
of the GNAT Ada and C preprocessors,
of a multi-language project.
In this example we describe through the GPRs:
the source file containing the entry point,
the definition of a type of build and a variable of the same type,
the languages used,
the directory where the compilation products should be stored,
the directory containing the generated binary and library,
the settings to be applied to the compilers.
The generated application performs the convolution of an image. The framework is in Ada 2012 and the code implementing the convolution product is in C. We want to be able to have a DEBUG build and a RELEASE build of the application. These two types of build are different both for the generation parameters and for the behavior of the program they produce.
The project tree is as follows:
With:
config.gpr: “abstract” GPR allowing to share a number of definitions,
test_convol.gpr: “normal” GPR generating the executable “test_convol.exe“,
convol.gpr: “library” GPR generating the static library “libconvol.a“,
test_convol.adb: Ada entry point of the application,
image.ad[sb]: package defining an image and implementing its services,
convol.c: implementation of the convolution product.
Note
In the following, in simple cases the Debug
pragma can also satisfy
the same need regarding the behavior of the program depending upon
the type of build 2.
It is quite common to define a DEBUG
and RELEASE
build as well as a NATIVE
and CROSS
target.
The program is succinctly implemented as follows:
config.gpr¶
Through this “abstract” GPR, we will define both the build type and the definitions shared between GPRs.
Specifically, we will define what is a DEBUG
build and a RELEASE
build as well as the associated compiler options.
For the DEBUG
build, we set the macros __DEBUG__
and
Build_Kind
that will be taken into account during pre-processing
of the source files.
For the RELEASE
build, we will consider that all compiler warnings
are errors.
In the end the config.gpr
looks like:
convol.gpr¶
With this “library” GPR, we will provide the
information required for the construction of the static library
libconvol.a
. We also note that the compiler configuration is
the one defined by the config GPR.
test_convol.gpr¶
With this “normal” GPR, we will define the Ada entry point of our program. Like the previous GPR, it relies on the compiler configuration defined by the config GPR.
To generate the application you can either use the GPS IDE and select the desired build in the scenario view.
Or call the GPRbuild
tool directly from
the command line by specifying the desired build.
In both cases, the compilation products will be the same:
Conclusion¶
The GNAT GPR technology provides an efficient and scalable solution for the implementation of projects that can be both large and complex. This technology can be integrated into a development environment very easily.
Comments