Managing Sources
This chapter covers how GPR locates source files: directory scanning, explicit file lists, exclusions, and naming conventions. Understanding these mechanisms lets you organize your source tree freely without forcing a particular directory layout.
How source discovery works
When a project is loaded, GPR tools scan every directory listed in
Source_Dirs and collects all files whose extensions match the active
languages. For Ada, the default extensions are .ads (specs) and .adb
(bodies); for C, .c and .h; and so on.
Files that do not match any known extension for the declared languages are silently ignored.
Source directories
Source_Dirs accepts a list of directory paths, relative to the project
file. Glob patterns are expanded at load time:
for Source_Dirs use ("src", "generated/src", "platform/" & Platform);
The ** pattern matches any number of directory levels, enabling a
recursive scan:
for Source_Dirs use ("src/**");
This collects sources from src/ and every subdirectory beneath it,
however deeply nested.
To collect sources only from the project file’s own directory, use a single dot:
for Source_Dirs use (".");
To declare a project with no sources - one that exists purely to share
attributes with other projects - use the abstract qualifier:
abstract project Support is
-- no sources; attributes only
end Support;
An abstract project may not declare Main, Object_Dir, or
Exec_Dir; those attributes are meaningful only for projects that produce
build artifacts. See Project Extension for a typical use of abstract
projects.
Tip
Avoid overlapping Source_Dirs between two projects in the same tree.
If the same source file is visible to two projects, a duplicate-unit
error is reported at load time.
Explicit source lists
For finer control, you can enumerate sources explicitly instead of scanning a directory.
Source_Files lists individual file names (not paths) directly in the
project file:
for Source_Files use ("utils.ads", "utils.adb", "config.ads");
Only the named files are included, even if other source files exist in
Source_Dirs.
For larger lists, Source_List_File points to a text file containing one
file name per line:
for Source_List_File use "sources.txt";
This is useful when the list is generated by a build script or version control system.
Excluding sources
Excluded_Source_Files removes specific files from an otherwise
directory-scanned project:
for Excluded_Source_Files use ("old_impl.adb", "stub.ads");
The complement of Source_List_File, Excluded_Source_List_File points
to a text file listing the files to exclude:
for Excluded_Source_List_File use "excluded.txt";
Exclusions apply after the directory scan, so you can keep sources in the same directory without including all of them in the build.
Ada naming conventions
The default naming convention for Ada follows the GNAT standard:
Specs use the
.adsextension, bodies use.adb.Child unit names use
-as the separator: the body ofMy_Lib.Utilslives inmy_lib-utils.adb.Unit names map to file names in lower case.
Ada source files and unit names are tightly coupled: when a file with the
right suffix (.ads or .adb) is found in a source directory, GPR
derives the corresponding unit name by reversing the naming convention
(stripping the suffix, replacing the dot-replacement character back to
., and so on). If the result is not a valid Ada identifier, the file is
silently ignored.
For example, my_source__unix.adb maps to my_source__unix - a name
containing a double underscore, which is not valid in Ada. GPR will not
recognize this file as an Ada source during directory scanning.
Tip
To include a file whose name would otherwise be rejected, add an explicit
per-unit mapping in the Naming package (see Per-unit overrides
below).
If your project uses a different convention, the Naming package lets
you override it.
Changing the dot replacement character
To use __ instead of - as the child-unit separator:
package Naming is
for Dot_Replacement use "__";
end Naming;
With this setting, the body of My_Lib.Utils is expected in
my_lib__utils.adb.
Changing the default suffixes
package Naming is
for Spec_Suffix ("Ada") use ".1.ada";
for Body_Suffix ("Ada") use ".2.ada";
end Naming;
Per-unit overrides
Individual units can be mapped to arbitrary file names:
package Naming is
for Spec ("My_Lib.Utils") use "ml_utils_s.ada";
for Body ("My_Lib.Utils") use "ml_utils_b.ada";
end Naming;
Per-unit mappings take precedence over suffix rules. This is the mechanism GPRname uses when it generates a project file for a source tree with non-standard naming (see Working with Tools).
Multiple suffixes per language
Body_Suffix and Spec_Suffix accept a single value per language. A
project that contains source files with different suffixes for the same
language - for example .cc and .cpp both for C++ - cannot be
described by a single suffix declaration.
Two options are available:
Rename all files to a common suffix. This requires no project structure changes and is the simplest solution when the source tree is under your control.
Split into two projects with
limited with. Create a companion project that covers the extra suffix, sharing the sameSource_DirsandObject_Diras the primary project. The companion imports the primary with a regularwith; the primary references the companion back withlimited withto break what would otherwise be a circular dependency. The companion also setsSpec_Suffixto a value that matches no file, so it does not claim spec files already owned by the primary. The compiler settings are shared by renaming the package:-- prj.gpr (handles .cpp files) limited with "prj_cc_support"; project Prj is for Languages use ("C++"); for Source_Dirs use ("src"); for Object_Dir use "obj"; package Naming is for Body_Suffix ("C++") use ".cpp"; for Spec_Suffix ("C++") use ".h"; end Naming; package Compiler is for Switches ("C++") use ("-O2"); end Compiler; end Prj;
-- prj_cc_support.gpr (handles .cc files) with "prj"; project Prj_Cc_Support is for Languages use ("C++"); for Source_Dirs use Prj'Source_Dirs; for Object_Dir use Prj'Object_Dir; package Naming is for Body_Suffix ("C++") use ".cc"; for Spec_Suffix ("C++") use ".no_match"; -- no .no_match files exist end Naming; package Compiler renames Prj.Compiler; end Prj_Cc_Support;
Build with
gprbuild -P prj.gpr: GPRbuild loads both projects and compiles.cppfiles viaPrjand.ccfiles viaPrj_Cc_Support, depositing all objects in the sharedobj/directory.