Previous: Importing Other Projects, Up: Examples of Project Files


11.2.4 Extending a Project

In large software systems it is common to have multiple implementations of a common interface; in Ada terms, multiple versions of a package body for the same specification. For example, one implementation might be safe for use in tasking programs, while another might only be used in sequential applications. This can be modeled in GNAT using the concept of project extension. If one project (the “child”) extends another project (the “parent”) then by default all source files of the parent project are inherited by the child, but the child project can override any of the parent's source files with new versions, and can also add new files. This facility is the project analog of a type extension in Object-Oriented Programming. Project hierarchies are permitted (a child project may be the parent of yet another project), and a project that inherits one project can also import other projects.

As an example, suppose that directory /seq contains the project file seq_proj.gpr as well as the source files pack.ads, pack.adb, and proc.adb:

     /seq
       pack.ads
       pack.adb
       proc.adb
       seq_proj.gpr

Note that the project file can simply be empty (that is, no attribute or package is defined):

     project Seq_Proj is
     end Seq_Proj;

implying that its source files are all the Ada source files in the project directory.

Suppose we want to supply an alternate version of pack.adb, in directory /tasking, but use the existing versions of pack.ads and proc.adb. We can define a project Tasking_Proj that inherits Seq_Proj:

     /tasking
       pack.adb
       tasking_proj.gpr
     
     project Tasking_Proj extends "/seq/seq_proj" is
     end Tasking_Proj;

The version of pack.adb used in a build depends on which project file is specified.

Note that we could have obtained the desired behavior using project import rather than project inheritance; a base project would contain the sources for pack.ads and proc.adb, a sequential project would import base and add pack.adb, and likewise a tasking project would import base and add a different version of pack.adb. The choice depends on whether other sources in the original project need to be overridden. If they do, then project extension is necessary, otherwise, importing is sufficient.

In a project file that extends another project file, it is possible to indicate that an inherited source is not part of the sources of the extending project. This is necessary sometimes when a package spec has been overloaded and no longer requires a body: in this case, it is necessary to indicate that the inherited body is not part of the sources of the project, otherwise there will be a compilation error when compiling the spec.

For that purpose, the attribute Locally_Removed_Files is used. Its value is a string list: a list of file names.

     project B extends "a" is
        for Source_Files use ("pkg.ads");
        --  New spec of Pkg does not need a completion
        for Locally_Removed_Files use ("pkg.adb");
     end B;

Attribute Locally_Removed_Files may also be used to check if a source is still needed: if it is possible to build using gnatmake when such a source is put in attribute Locally_Removed_Files of a project P, then it is possible to remove the source completely from a system that includes project P.