Project Tree

A GPR project file rarely exists in isolation. A system is typically split across several project files, each responsible for one component. This chapter describes how multiple project files are connected into a project tree.

Project tree structure

A GPR project file rarely exists in isolation. A system is typically split across several project files, each responsible for one component. The connected graph of project files forms the project tree.

The project tree is a directed acyclic graph (DAG) rooted at the root project - the project passed directly to the build tool (for example gprbuild -P root.gpr). Two kinds of edges connect projects in the tree:

Import (with clause)

The importing project depends on the imported project for compilation and for attribute visibility.

Extension (extends clause)

The extending project inherits sources and attributes from a base project. See Project Extension.

Aggregation (Project_Files) is a third relationship specific to aggregate and aggregate library projects; see Aggregate Project.

Every project in the tree is loaded once and shared, regardless of how many other projects import it.

The with clause

A project declares its imports at the top of the file, before the project declaration, using with clauses:

with "libs/utils/utils.gpr";
with "libs/crypto/crypto.gpr";

project My_App is
   for Source_Dirs use ("src");
   for Main        use ("main.adb");
end My_App;

Multiple paths may appear in a single clause, separated by commas:

with "utils.gpr", "crypto.gpr";

A given project may appear at most once across all with clauses of the same importing project. Duplicates are an error.

What a plain with makes visible

From within the body of the importing project, a plain with exposes all of the following from the imported project:

Types and variables

Typed string type definitions and variable declarations are accessible via the Imported_Project.Name notation.

Attributes and packages

Attribute values may be read using Imported_Project'Attribute (or Imported_Project.Package'Attribute). Packages may be renamed into the importing project with a package P renames Imported_Project.P declaration.

Source files

Sources of the imported project are compiled as needed; the resulting object files and Ada Library Information files (*.ali) are made available to the importing project’s sources.

Example - sharing settings from an abstract project:

with "common.gpr";

project My_App is
   --  Read a typed variable from Common
   Mode : Common.Build_Mode_Type := Common.Build_Mode;

   --  Alias the whole Compiler package from Common
   package Compiler renames Common.Compiler;

   --  Extend the linker switches defined in Common
   package Linker is
      for Switches ("Ada") use
         Common.Linker'Switches ("Ada") & ("-lm");
   end Linker;
end My_App;

Path resolution

The path in a with clause is a string naming a .gpr file. The .gpr extension is optional and appended automatically if absent. The directory separator is always /, even on Windows.

Paths are resolved in the following order:

  1. If the path is absolute, it is used directly.

  2. Otherwise it is resolved relative to the directory of the importing project file.

  3. If no file is found there, each directory listed in GPR_PROJECT_PATH is searched in order.

  4. Then the directories in ADA_PROJECT_PATH are searched.

  5. Finally the default project paths provided by the selected toolchain(s) are searched.

The first match terminates the search. Installed libraries typically rely on steps 3-5, so they can be imported by bare name without a path:

with "gnatcoll.gpr";   -- resolved via GPR_PROJECT_PATH or toolchain paths

The limited with clause

Because attribute evaluation must be acyclic, a cycle formed entirely from plain with edges is an error. The limited with variant relaxes this constraint by restricting what is visible across the import:

limited with "component_b.gpr";

project Component_A is
   for Source_Dirs use ("src_a");
end Component_A;

With a limited with:

Visible

Source files of the imported project are compiled as part of the tree; their object files and ALI files are available to the importing project’s sources.

Not visible

The imported project’s types, variables, attributes, and packages cannot be referenced inside the importing project’s body.

The limited modifier applies to the attribute-visibility graph only; it has no effect on the source-dependency graph. Two components whose Ada units are mutually recursive can therefore coexist in the same tree:

--  component_a.gpr
limited with "component_b.gpr";
project Component_A is
   for Source_Dirs use ("src_a");
end Component_A;

--  component_b.gpr
with "component_a.gpr";
project Component_B is
   for Source_Dirs use ("src_b");
end Component_B;

Here Component_B may reference attributes of Component_A, but Component_A may not reference attributes of Component_B.

A cycle requires at least one limited with edge to break the attribute cycle; without one the project loader reports an error.

Note

A need for limited with sometimes signals that the two components share a common abstraction that should be factored into a third project on which both depend. Consider restructuring before reaching for limited with.