Project File Language
GPR project files use an Ada-like syntax. This chapter describes the lexical rules, the structure of a project file, and the language constructs available within it.
Overview
The fundamental purpose of a project file is to supply attribute values
to GPR tools. An attribute is a named configuration parameter - source
directories, compiler switches, library name, and so on - declared with a
for clause (see Attribute Declarations). Attributes may be
indexed by a key such as a language name or source file name, and are
grouped into packages that namespace them by tool
(Compiler'Switches, Linker'Switches, …).
Typed variables and case statements let attribute values vary by
configuration without duplicating the project file.
Lexical Elements
Identifiers
Identifiers follow the same rules as Ada identifiers: they must start with a
letter and may be followed by letters, digits, or underscores. Two consecutive
underscores are not allowed. Identifiers are case-insensitive
("Name" and "name" are the same identifier).
simple_name ::= identifier
name ::= simple_name { . simple_name }
attribute_name ::= identifier
package_name ::= identifier
project_name ::= name
type_name ::= identifier
variable_name ::= identifier
project_ref ::= project_name
package_ref ::= package_name
| project_name . package_name
variable_ref ::= variable_name
| package_name . variable_name
| project_name . variable_name
| project_name . package_name . variable_name
attribute_ref ::= 'project' ''' attribute_name [ '(' string_expression ')' ]
| project_ref ''' attribute_name [ '(' string_expression ')' ]
| package_ref ''' attribute_name [ '(' string_expression ')' ]
builtin_name ::= 'external' | 'external_as_list' | 'file_as_list'
| 'lower' | 'upper' | 'split' | 'match' | 'filter_out'
| 'item_at' | 'remove_prefix' | 'remove_suffix'
| 'default' | 'alternative'
Strings
String literals are written between double quotes, as in Ada. They are in general case-sensitive, except where noted (for example, file names are case-insensitive on systems with case-insensitive file systems).
Reserved Words
The following Ada 95 reserved words are used in project files:
abstract all at case
end for is limited
null others package renames
type use when with
The following identifiers are additional reserved words specific to project files:
extends external external_as_list file_as_list project
alternative default filter_out item_at lower match
remove_prefix remove_suffix split upper
Note that aggregate and library are qualifiers that may precede the
keyword project but are not reserved words.
Project File Structure
A project file consists of an optional context clause followed by a project declaration.
project ::= context_clause project_declaration
context_clause ::= { with_clause }
with_clause ::= [ 'limited' ] 'with' path_name { , path_name } ;
path_name ::= string_literal
project_declaration ::= simple_project_declaration
| project_extension
simple_project_declaration ::=
[ qualifier ] 'project' project_name 'is'
{ declarative_item }
'end' project_name ;
project_extension ::=
[ qualifier ] 'project' project_name 'extends' [ 'all' ] path_name 'is'
{ declarative_item }
'end' project_name ;
qualifier ::= 'abstract'
| 'library'
| 'aggregate'
| 'aggregate' 'library'
| 'configuration'
| 'standard'
For the semantics of each qualifier see Project Kinds. For the
semantics of extends and extends all see Project Extension.
Context Clauses
context_clause ::= { with_clause }
with_clause ::= [ 'limited' ] 'with' path_name { , path_name } ;
path_name ::= string_literal
A project may import other projects via with clauses. Imported projects
make their sources and attributes visible to the importing project.
A limited with allows two projects to have mutual visibility of each
other’s compiled objects without creating a cycle in the attribute-dependency
graph. A project imported via limited with contributes its compiled
artifacts (object files, libraries) but its attributes cannot be referenced
by the importing project. This keeps attribute resolution strictly acyclic
while still permitting the mutual-view pattern needed by some system
architectures.
Path names in with clauses are strings containing a path to a .gpr
file. The .gpr extension is optional and will be added automatically if
absent. Paths may be absolute or relative. Relative paths are resolved with
respect to the directory of the current project file, or against directories
listed in GPR_PROJECT_PATH and ADA_PROJECT_PATH. The
directory separator may always be / even on Windows.
A given project name may appear only once across all with clauses of the
same project. Cycles without limited with are forbidden.
Child projects
A project whose name is a dotted name (Parent.Child) is a child project
of Parent. This is purely a naming convention expressing a close
relationship between the two; a child project does not implicitly import
its parent. An explicit with or extends clause is required.
The child project file name uses a dash in place of the dot:
Math_Proj.Tests lives in math_proj-tests.gpr.
-- math_proj-tests.gpr
with "math_proj.gpr";
project Math_Proj.Tests is
-- Parent variables and attributes accessible via the parent name prefix:
Obj_Dir := Math_Proj.Object_Dir;
...
end Math_Proj.Tests;
Once the parent is imported or extended, its variables and attributes are
accessible using the parent project name as a prefix (e.g.
Math_Proj.Object_Dir).
Source ownership
Each project directly owns a set of immediate sources: files identified through its source-related attributes (source directories, explicit file lists, etc.). The full set of sources visible to a project extends this with the immediate sources of every project it depends on, directly or indirectly (unless overridden by extension). For the rules governing basename uniqueness, search order, and source shadowing in extensions, see Source Resolution.
Declarations
Declarations introduce types, variables, attributes, and packages. They are processed sequentially in the order they appear; a name becomes visible immediately after its declaration.
declarative_item ::= simple_declarative_item
| typed_string_declaration
| package_declaration
simple_declarative_item ::= variable_declaration
| typed_variable_declaration
| attribute_declaration
| case_construction
| empty_declaration
empty_declaration ::= 'null' ;
An empty declaration (null;) is valid anywhere a declaration is allowed
and has no effect.
Scope rules:
Declarations are scoped to either the project level or the enclosing package. The following rules apply:
Typed string declarations may only appear at the project level. Once declared, the type is visible throughout the entire project file, including inside packages, and may be referenced from other projects using a qualified name (
Project_Name.Type_Name).Package declarations may only appear at the project level; packages cannot be nested.
Variable declarations (typed or untyped) may appear at the project level or inside a package. A project-level variable is visible throughout the project file. A variable declared inside a package is local to that package and can be referenced from outside only via a qualified name (
Package_Name.Variable_NameorProject_Name.Package_Name.Variable_Name).Attribute declarations may appear at the project level (setting project-level attributes) or inside a package (setting package attributes). Attributes from other projects or packages are accessible via qualified names.
Case constructions may appear at both levels. The discriminant must be a typed variable already declared before the
caseconstruct. Inside a case arm, only attribute declarations, variable declarations (for variables already declared before thecase), nested case constructions, andnullare allowed - type and package declarations are forbidden.
Values
GPR projects manipulate two kinds of values:
- String
A single string, e.g.
"debug"or"src/main.adb".- List of strings
An ordered sequence of strings, e.g.
("-O2", "-g"). The empty list is written().
Attributes and variables each hold one of these two kinds. The kind of an attribute is fixed by the language specification (see Attributes); the kind of an untyped variable is inferred from its first declaration.
Values may incorporate previously declared variables and attributes via
references, call built-in functions, and may be combined using the &
concatenation operator. Once any operand is a list the result is a list, and
the list must be the left operand. See Built-in Functions and
Expressions for the formal grammar.
Built-in Functions
Built-in functions may be used inside expressions. Their names are not
reserved words and may be used as variable names elsewhere; a name is
interpreted as a built-in call only when immediately followed by (.
builtin_call ::= builtin_name '(' [ expression { , expression } ] ')'
A built-in call may return either a string or a list of strings depending on the function; see Built-in Functions for the specific signatures of each.
The External function
External retrieves a string value from the build environment. The first
argument is the name of an external variable; the optional second argument is
the default value. Its value is resolved, in priority order, from:
The
-X\ *name*\ =\ *value*command-line switch.An environment variable of the same name.
The second argument, if supplied (the default value).
If none of these sources provides a value, an error is reported.
External is typically used to initialize typed variables (see
Typed String Declaration), which are then referenced in case
constructions to vary attribute values across build scenarios. Such variables
are commonly called scenario variables.
type Build_Mode_Type is ("debug", "release");
Build_Mode : Build_Mode_Type := external ("BUILD_MODE", "debug");
The External_As_List function
The External_As_List function retrieves a list of strings from the
environment by splitting an external variable on a separator.
external_as_list_value ::=
'external_as_list' ( string_literal , string_literal )
The first argument is the external variable name; the second is the separator string. The value is looked up on the command line first, then as an environment variable. If undefined, an empty list is returned (no error). Leading and trailing separators are discarded.
Key differences from External:
No default-value parameter; returns
()when the variable is undefined.Returns a list, not a string.
Example:
-- If SWITCHES is "-O2,-g", External_As_List ("SWITCHES", ",")
-- returns ("-O2", "-g").
The File_As_List function
Note
This function is not available in tools based on the legacy GPR1 framework.
File_As_List reads a text file and returns its lines as a list of strings.
list ::= 'file_as_list' ( string_literal )
The argument is the path to the file. Returns () if the file does not
exist or is empty.
Source_Files := file_as_list ("sources.txt");
String Manipulation Functions
Note
The functions in this section are not available in tools based on the legacy GPR1 framework. This includes all tools that have not yet migrated to the GPR2 library.
Split
Splits a string on a separator and returns the parts as a list. Empty parts
are not included. Returns () if the string is empty or consists entirely
of separators.
Split ( string_literal , string_literal )
-- Split ("-gnatf,-gnatv", ",") => ("-gnatf", "-gnatv")
Lower / Upper
Return the argument with all characters converted to lower or upper case. Accept a string or a list.
-- Lower ("FOO") => "foo"
-- Upper (("one","two")) => ("ONE", "TWO")
Remove_Prefix / Remove_Suffix
Remove a fixed prefix or suffix from a string or from each element of a list, if present.
-- Remove_Prefix (("libone", "two", "libthree"), "lib")
-- => ("one", "two", "three")
-- Remove_Suffix ("libZ.so", ".so") => "libZ"
Filter_Out
Removes from a list all elements matching a regular-expression pattern.
-- Filter_Out (("value1", "or", "another", "one"), ".*o.*")
-- => ("value1")
Match
Returns elements of a string or list that match a regular-expression pattern. An optional third argument provides a replacement pattern applied to each match.
-- Match ("x86_64-linux-gnu", "linux") => "linux"
-- Match (("value1","or","another","one"), "(.*r)", "r:\1")
-- => ("r:or", "r:another")
Item_At
Returns one element from a list by index. Negative indices count from the
end ("-1" is the last element).
-- Item_At (("one","two","three","last"), "2") => "two"
-- Item_At (("one","two","three","last"), "-1") => "last"
Default / Alternative
Default returns its second argument when the first is the empty string;
otherwise it returns the first argument.
Alternative returns its second argument when the first is not the empty
string; otherwise it returns the first argument.
-- Default ("", "fallback") => "fallback"
-- Default ("x", "fallback") => "x"
-- Alternative ("", "fallback") => ""
-- Alternative ("x", "fallback") => "fallback"
Expressions
An expression builds a value from literals, variable references, attribute references, built-in function calls, and concatenation:
string_literal ::= "{string_element}" -- Same as Ada
string_expression ::= string_literal
| variable_ref
| attribute_ref
| builtin_call
| string_expression & string_expression
string_list ::= ( string_expression { , string_expression } )
| variable_ref
| attribute_ref
| builtin_call
term ::= string_expression | string_list
expression ::= term { & term }
Concatenation rules follow Ada conventions. Once any operand is a list, the result is a list:
function "&" (X : String; Y : String) return String;
function "&" (X : String_List; Y : String) return String_List;
function "&" (X : String_List; Y : String_List) return String_List;
Example:
List := () & File_Name; -- one element
List2 := List & (File_Name & ".orig"); -- two elements
Big := List & List2; -- three elements
-- Illegal := "gnat.adc" & List2; -- error: list must be left operand
Typed String Declaration
A type declaration introduces a finite set of string literals. Variables declared with this type are restricted to the listed values. Type declarations may only appear at the project level, not inside a package.
typed_string_declaration ::=
'type' type_name 'is'
( string_literal { , string_literal } ) ;
String literals in the list are case-sensitive and must be distinct. Example:
type OS is ("GNU/Linux", "Unix", "Windows", "VMS");
Variables of a string type are called typed variables; all others are
untyped variables. A type declared in another project may be referenced
using a qualified name (Project_Name.Type_Name).
Variables
Variables store a string or list-of-strings value and may appear in
expressions. A variable declaration creates the variable and assigns it a
value. Before its first declaration, a variable defaults to "" (empty
string).
Typed Variables
A typed variable must be declared exactly once. Its type restricts the
values it may hold, and because it can only be set once, all case
constructions in the file see a consistent value - making it behave
effectively as a constant.
typed_variable_declaration ::=
variable_name : type_name := string_expression ;
type OS_Type is ("GNU/Linux", "Unix", "Windows");
OS : OS_Type := external ("OS", "GNU/Linux");
Untyped Variables
An untyped variable may be declared and reassigned any number of times. Its kind (string or list) is fixed by the first declaration; subsequent declarations must match.
variable_declaration ::= variable_name := expression ;
Name := "readme.txt";
Save_Name := Name & ".saved";
Flags := ("-O2", "-g");
Variable References
A variable may be referenced by its simple name or a qualified name
(variable_ref as defined in Identifiers):
Mode -- variable in current scope
Compiler.Opt_Level -- variable in a package
Common.Build_Mode -- variable in an imported project
Common.Compiler.Opt_Level -- variable in a package of an imported project
A simple name resolves to the current package (if any) or the current project. Qualified names may refer to a package in the current project, an imported project, a base project (direct or indirect), or a package within any of those.
Attribute Declarations
Attributes are the primary mechanism for communicating information to build
tools. An attribute declaration uses the for ... use syntax:
attribute_declaration ::=
'for' attribute_name 'use' expression ;
| 'for' attribute_name '(' string_expression ')' 'use' expression ;
The optional parenthesised string expression is the index of the attribute. Indexed attributes associate different values with different keys - for example, per-language compiler switches:
package Compiler is
for Switches ("Ada") use ("-O2", "-gnat2022");
for Switches ("C") use ("-O2", "-Wall");
end Compiler;
An attribute may also be declared without an index, in which case it has a single value for the whole project or package:
for Library_Name use "mylib";
Different attributes accept different kinds of indexes:
- Language (case-insensitive)
A language identifier such as
"Ada"or"C". Used by mostCompiler,Binder,Linker, andNamingattributes.for Compiler'Switches ("Ada") use ...;- File / Glob (case-sensitivity host-dependent)
A source file simple name or a glob pattern (
*,?,[]). When a glob pattern is used, the attribute applies to every source file whose name matches that pattern. Some attributes also acceptothersas an index, which matches any source file not matched by a more specific index in the same project. Whetherothersis accepted is specified per attribute in Attributes.for Compiler'Switches ("main.adb") use ...;for Compiler'Switches ("*.c") use ...;- File / Glob / Language (case-sensitivity host-dependent for files, case-insensitive for languages)
Accepts a source file simple name, a glob pattern, or a language identifier. Strings containing dots or glob characters (
*,?,[,]) are treated as file names or glob patterns; all other strings are treated as language identifiers. Some attributes also acceptothersas a catch-all index. Whetherothersis accepted is specified per attribute in Attributes.When multiple declarations for the same attribute exist in a project with different indexes, the value that applies to a given source file is resolved in the following priority order:
An index that is an exact match on the file name.
An index that is a glob pattern matching the file name.
An index matching the file’s language.
The
othersindex, if present.
Used by
Compiler'SwitchesandRoots.- Unit (case-insensitive)
An Ada unit name. Used by
Naming'SpecandNaming'Body.for Naming'Spec ("My.Package") use "my-package.ads";- String
An arbitrary string key. Used for instance by the aggregate-project attribute
External, where the index is the name of an external variable.for External ("BUILD_MODE") use "release";
Attribute references (attribute_ref as defined in Identifiers) allow
reading values from other projects or packages:
for Switches ("Ada") use Common.Compiler'Switches ("Ada") & ("-g");
For the full list of attributes and their semantics, see Attributes. Individual tools may define additional attributes in their own packages; refer to each tool’s documentation.
Packages
A project file may contain packages, which group related attributes - typically all attributes used by one tool. A given package name may appear at most once per project file.
package_declaration ::= package_spec | package_renaming | package_extension
package_spec ::=
'package' package_name 'is'
{ simple_declarative_item }
'end' package_name ;
The standard packages recognized in all project files are:
BinderOptions for the binder (
gnatbind/ GPRbuild).BuilderGlobal build options (executable names, global compilation switches).
CleanOptions for
gprclean.CompilerCompilation options per language.
InstallOptions for
gprinstall.LinkerLink options.
NamingSource-file naming conventions.
Other tool-specific packages may be defined by individual tools; refer to each tool’s documentation.
Note
Each tool only reads the packages it recognizes; unknown package
declarations are silently ignored. However, referencing an attribute or
variable from an unknown package in an expression - for example, reading
Clean'Switches inside a project loaded by GPRbuild - will cause a
parsing error and the project will be rejected. Avoid cross-package
attribute references unless you can guarantee that every tool loading the
project knows the referenced package.
A minimal (empty) package:
project Simple is
package Builder is
end Builder;
end Simple;
A package may contain attribute declarations, variable declarations, and case constructions.
Note
When a name could refer to either a project or a package, it always
designates the project. Avoid naming projects after standard package names
or names starting with gnat.
Package Renaming
A package may be defined by a renaming declaration, which gives the new package the same attributes as a package declared in another project. The renamed project must appear in the current project’s context clause (or be its base project). No attributes may be added or overridden in a renaming; use a package extension for that.
package_renaming ::=
'package' package_name 'renames'
project_name . package_name ;
Package renaming is a common way to share settings: define the authoritative package in one project file and rename it in all projects that need the same settings.
Package Extension
A package extension works like a renaming but also allows adding or overriding attributes. It is particularly useful in project extension: a package inherited from the base project can be explicitly extended to add or override specific attributes without replacing it entirely.
package_extension ::=
'package' package_name 'extends'
project_name . package_name 'is'
{ simple_declarative_item }
'end' package_name ;
Case Constructions
A case construction selects attribute and variable declarations based on
the value of a typed variable, enabling conditional project configuration.
case_construction ::=
'case' variable_ref 'is' { case_item } 'end' 'case' ;
case_item ::=
'when' discrete_choice_list '=>'
{ case_construction
| attribute_declaration
| variable_declaration
| empty_declaration }
discrete_choice_list ::= string_literal { '|' string_literal } | 'others'
Rules:
All choices must be distinct.
All values of the type must be covered, either explicitly or via
others.othersmust be the last alternative.The case expression must be a variable (typed or untyped) whose value is known at load time.
Inside a
case, only case constructions, attribute declarations, variable declarations (for already-declared variables), andnulldeclarations are allowed. Type and package declarations are not.
Example:
project MyProj is
type OS_Type is ("GNU/Linux", "Unix", "Windows", "VMS");
OS : OS_Type := external ("OS", "GNU/Linux");
package Compiler is
case OS is
when "GNU/Linux" | "Unix" =>
for Switches ("Ada") use ("-gnath");
when "Windows" =>
for Switches ("Ada") use ("-gnatP");
when others =>
null;
end case;
end Compiler;
end MyProj;
Comments
Comments follow Ada syntax: two consecutive hyphens (
--) start a comment that extends to the end of the line.