2.9.1. Project Declaration

Project files have an Ada-like syntax. The minimal project file is:

project Empty is
end Empty;

The identifier Empty is the name of the project. This project name must be present after the reserved word end at the end of the project file, followed by a semicolon.

Identifiers (i.e., the user-defined names such as project or variable names) have the same syntax as Ada identifiers: they must start with a letter, and be followed by zero or more letters, digits or underscore characters; it is also illegal to have two underscores next to each other. Identifiers are always case-insensitive ("Name" is the same as "name").

simple_name ::= identifier
name        ::= simple_name { . simple_name }

Strings are used for values of attributes or as indexes for these attributes. They are in general case sensitive, except when noted otherwise (in particular, strings representing file names will be case insensitive on some systems, so that "file.adb" and "File.adb" both represent the same file).

Reserved words are the standard Ada 95 reserved words, plus several others listed below, and cannot be used for identifiers. In particular, the following Ada 95 reserved words are currently used in project files:

abstract  all     at       case
end       for     is       limited
null      others  package  renames
type      use     when     with

The additional project file reserved words are:

extends external external_as_list project

Note that aggregate and library are qualifiers that may appear before the keyword project, but they are not themselves keywords.

To avoid possible compatibility issues in the future, we recommend that the reserved words introduced by Ada 2005 and Ada 2012 not be used as identifiers in project files. Note also that new reserved words may be added to the project file syntax in a later release.

Comments in project files have the same syntax as in Ada, two consecutive hyphens through the end of the line.

A project may be an independent project, entirely defined by a single project file. Any source file in an independent project depends only on the predefined library and other source files in the same project. Alternatively, a project may depend on other projects in various ways:

  • by importing them through context clauses (with clauses), or

  • by extending at most one other project (its base project).

A given project may exhibit either or both of these dependencies; for example:

with "imported_proj.gpr";
project My_Project extends "base_proj.gpr" is
end My_Project;

The import dependencies form a directed graph, potentially cyclic when using limited with. The subgraph reflecting the extends relationship is a tree (hierarchy).

A path name denotes a project file. It can be absolute or relative. An absolute path name includes a sequence of directories, in the syntax of the host operating system, that uniquely identifies the project file in the file system. A relative path name identifies the project file, relative to the directory that contains the current project, or relative to a directory listed in the environment variables ADA_PROJECT_PATH and GPR_PROJECT_PATH. Path names are case sensitive if file names in the host operating system are case sensitive. As a special case, the directory separator can always be '/' even on Windows systems, so that project files can be made portable across architectures. The syntax of the environment variables ADA_PROJECT_PATH and GPR_PROJECT_PATH is a list of directory names separated by colons on Unix and semicolons on Windows.

A given project name can appear only once in a context clause, and may not appear in different context clauses for the same project.

It is illegal for a project imported by a context clause to refer, directly or indirectly, to the project in which this context clause appears (the dependency graph cannot contain cycles), except when one of the with clauses in the cycle is a limited with.

A project’s immediate sources are the source files directly defined by that project, either implicitly by residing in the project source directories, or explicitly through any of the source-related attributes. More generally, a project’s sources are the immediate sources of the project together with the immediate sources (unless overridden) of any project on which it depends directly or indirectly.

 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' ] <base_project_>name 'is'
    {declarative_item}
  'end' <project_>name ;

qualifier ::=
  'abstract' | identifier [ identifier ]

2.9.2. Qualified Projects

Immediately preceding the reserved project, a qualifier may be specified which identifies the nature of the project. The following qualifiers are allowed:

standard:

A standard project is a non-library project with source files. This is the default (implicit) qualifier.

abstract:

A project with no source files. Such a project must either have no declaration for attributes Source_Dirs, Source_Files, Languages or Source_List_File, or one of Source_Dirs, Source_Files, or Languages must be declared as empty. If it extends another project, the base project must also be an abstract project.

aggregate:

A project whose sources are aggregated from other project files.

aggregate library:

A library whose sources are aggregated from other project or library project files.

library:

A library project must define both of the attributes Library_Name and Library_Dir.

configuration:

A configuration project cannot be in a project tree. It describes compilers and other tools to gprbuild.

2.9.3. Declarations

Declarations introduce new entities that denote types, variables, attributes, and packages. Some declarations can only appear immediately within a project declaration. Others can appear within a project or within a package.

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 is allowed anywhere a declaration is allowed. It has no effect.

2.9.4. Packages

A project file may contain packages, which group attributes (typically all the attributes that are used by one of the GNAT tools).

A package with a given name may only appear once in a project file. The following packages are currently always recognized in project files (See Attributes for the list of attributes that each can contain).

Binder

This package specifies characteristics useful when invoking the binder either directly via the gnat driver or when using GPRbuild. See Main Subprograms.

Builder

This package specifies the compilation options used when building an executable or a library for a project. Most of the options should be set in one of Compiler, Binder or Linker packages, but there are some general options that should be defined in this package. See Main Subprograms, and Executable File Names in particular.

Clean

This package specifies the options used when cleaning a project or a project tree using the tools gnatclean or gprclean.

Compiler

This package specifies the compilation options used by the compiler for each language. See Tools Options in Project Files.

Gnatls

This package specifies the options to use when invoking gnatls via the gnat driver.

Install

This package specifies the options used when installing a project with gprinstall. See Package Install Attributes.

Linker

This package specifies the options used by the linker. See Main Subprograms.

Naming

This package specifies the naming conventions that apply to the source files in a project. In particular, these conventions are used to automatically find all source files in the source directories, or given a file name to find out its language for proper processing. See Naming Schemes.

Other tool-specific packages may be defined by different project-aware tools. Refer to the tool’s documentation for the list of supported attributes and other specifics.

In its simplest form, a package may be empty:

project Simple is
  package Builder is
  end Builder;
end Simple;

A package may contain attribute declarations, variable declarations and case constructions, as will be described below.

When there is ambiguity between a project name and a package name, the name always designates the project. To avoid possible confusion, it is always a good idea to avoid naming a project with one of the names allowed for packages or any name that starts with gnat.

Package renaming

A package may be defined by a renaming declaration. The new package renames a package declared in a different project file, and has the same attributes as the package it renames. The name of the renamed package must be the same as the name of the renaming package. The project must contain a package declaration with this name, and the project must appear in the context clause of the current project, or be its base or parent project. It is not possible to add or override attributes to the renaming project. If you need to do so, you should use an extending declaration (see below).

Packages that are renamed in other project files often come from project files that have no sources: they are just used as templates. Any modification in the template will be reflected automatically in all the project files that rename a package from the template. This is a very common way to share settings between projects.

Package extension

A package can also be defined by an extending declaration. This is similar to a renaming declaration, except that it is possible to add or override attributes.

package_declaration ::= package_spec | package_renaming | package_extension

package_spec ::=
  'package' <package_>simple_name 'is'
     { simple_declarative_item }
  'end' package_identifier ;

package_renaming ::=
  'package' <package_>simple_name 'renames'
        <project_>simple_name.package_identifier ;

package_extension ::=
  'package' <package_>simple_name 'extends'
        <project_>simple_name.package_identifier 'is'
     { simple_declarative_item }
  'end' package_identifier ;

2.9.5. Expressions

An expression is any value that can be assigned to an attribute or a variable. It is either a literal value, or a construct requiring run-time computation by the Project Manager. In a project file, the computed value of an expression is either a string or a list of strings.

A string value is one of:

  • A literal string, for instance "comm/my_proj.gpr"

  • The name of a variable that evaluates to a string (see Variables)

  • The name of an attribute that evaluates to a string (see Attributes)

  • An external reference (see The function External)

  • A concatenation of the above, as in "prefix_" & Var.

A list of strings is one of the following:

  • A parenthesized comma-separated list of zero or more string expressions, for instance (File_Name, "gnat.adc", File_Name & ".orig") or ().

  • The name of a variable that evaluates to a list of strings

  • The name of an attribute that evaluates to a list of strings

  • A concatenation of a list of strings and a string (as defined above), for instance ("A", "B") & "C"

  • A concatenation of two lists of strings

The following is the grammar for expressions

string_literal ::= "{string_element}"  --  Same as Ada

string_expression ::= string_literal
    | <variable_>name
    | external_value
    | attribute_reference
    | ( string_expression { & string_expression } )

string_list  ::= ( string_expression { , string_expression } )
   | <string_variable>_name
   | <string_>attribute_reference

term ::= string_expression | string_list

expression ::= term { & term }     --  Concatenation

Concatenation involves strings and list of strings. As soon as a list of strings is involved, the result of the concatenation is a list of strings. The following Ada declarations show the existing operators:

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;

Here are some specific examples:

List := () & File_Name; --  One string in this list
List2 := List & (File_Name & ".orig"); -- Two strings
Big_List := List & Lists2;  --  Three strings
Illegal := "gnat.adc" & List2;  --  Illegal, must start with list

2.9.6. Built-in Functions

Built-in functions may be used in expressions. The names of built-in functions are not reserved words and may also be used as variable names. In an expression, a built-in function is recognized if its name is immediately followed by an open parenthesis (‘(‘).

2.9.6.1. The function Alternative

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

The function Alternative takes two arguments. It returns the second argument if the first one is not the empty string.

Alternative ("", "this is the default value")

=> ""

Alternative ("x86_64-linux-gnu", "linux")

=> "linux"

2.9.6.2. The function Default

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

The function Default takes two arguments. It returns the second argument if the first one is the empty string.

Default ("", "this is the default value")

=> "this is the default value"

Default ("One", "this is the default value")

=> "One"

2.9.6.3. The function External

An external value is an expression whose value is obtained from the command that invoked the processing of the current project file (typically a gprbuild command).

The syntax of a single string external value is:

external_value ::= 'external' ( string_literal [, string_literal] )

The first string_literal is the name of the external variable, whose value (a string) may be specified by an environment variable with this name, or on the command line via the -Xname=value option. The command line takes precedence if the name is defined in both contexts, thus allowing the user to locally override an environment variable. The second string_literal, if present, is the default to use if there is no specification for this external value either on the command line or in the environment. If the value of the external variable is not obtained from an environment variable or the command line, and the invocation of the external function does not supply a second parameter, then an error is reported.

An external reference may be part of a string expression or of a string list expression, and can therefore appear in a variable declaration or an attribute declaration.

This construct is typically used to initialize typed variables, which are then used in case constructions to control the value assigned to attributes in various scenarios. Thus such variables are often called scenario variables.

2.9.6.4. The function External_As_List

An external value is an expression whose value is obtained from the command that invoked the processing of the current project file (typically a gprbuild command).

The syntax for a string list external value is:

external_value ::= 'external_as_list' ( string_literal , string_literal )

The first string_literal is the name of the external variable, with the same interpretation as for the external function; it is looked up first on the command line (as the name in a -Xname=value option) and, if not so specified, then as an environment variable. If it is not defined by either of these, then the function returns an empty list. The second string_literal is the separator between each component of the string list. An empty list is returned if the separator is an empty string or if the external value is only one separator.

Any separator at the beginning or at the end of the external value is discarded. Then, if there is no separator in the external value, the result is a string list with only one string. Otherwise, any string between the beginning and the first separator, between two consecutive separators and between the last separator and the end are components of the string list.

Note the following differences between External and External_As_List:

  • The External_As_List function has no default value for the external variable

  • The External_As_List function returns an empty list, and does not report an error, when the value of the external variable is undefined.

These differences reflect the different use cases for the two functions. External variables evaluated by the External function are often used for configuration control, and misspellings should be detected as errors rather than silently returning the empty string. If the user intended an empty string as the result when the external variable was undefined, then this could easily be obtained:

External ("SOME_VAR", "")

In contrast, the External_As_List function more typically is used for external variables that may or may not have definitions (for example, lists of options or paths) and then the desired result in the undefined case is an empty list, not a reported error.

Here is an example of the External_As_List function:

External_As_List ("SWITCHES", ",")

If the external value of SWITCHES is "-O2,-g", the result is ("-O2", "-g").

If the external value is ",-O2,-g,", the result is also ("-O2", "-g").

if the external value is "-gnatv", the result is ("-gnatv").

If the external value is ",,", the result is ("").

If the external value is ",", the result is (), the empty string list.

2.9.6.5. The function Filter_Out

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

The function Filter_Out takes two arguments. The first argument must be a list and the second one a simple string. The second argument is a pattern (regular expression). Elements in the list matching the pattern will be removed from the list.

List := ("value1", "or", "another", "one");

Example removing all values containing the letter ‘o’:

Filter_Out (List, ".*o.*")

=> ("value1")

Example removing all values:

Filter_Out (List, ".*")

=> ()

2.9.6.6. The function Item_At

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

The function Item_At takes two arguments. The first argument must be a list and the second argument an integer image (simple string). The number represents the index of the item to return from the list. If the number is negative it is an index starting from the end of the list. That is, “-1” is the last list item.

List := ("one", "two", "three", "last");

Item_At (List, "2")

=> "two"

Item_At (List, "-1")

=> "last"

2.9.6.7. The function Lower

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

Function Lower takes a single argument which can be a simple string or a list. It returns the argument with all strings in lower case.

Example:

Lower ("The Lower Built-In")

=> "the lower built-in"

Example with a list:

List := ("One", "Two");

Lower (List)

=> ("one", "two")

2.9.6.8. The function Match

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

Function Match takes two mandatory arguments. The first argument is a simple string or a list. The second argument is the pattern (regular expression) to match. An optional third argument can be given which is the replacement pattern for the matching strings.

Example:

Match ("x86_64-linux-gnu", "linux")

=> "linux"

Example with a list and a replacement pattern:

List := ("value1", "or", "another", "one");

Match (List, "(.*r)", "r:\1")

=> ("r:or", "r:another")

In the above example we match all strings in List containing the letter ‘r’ and the result is formed with ‘r:’ as prefix concatenated with the matching string.

2.9.6.9. The function Remove_Prefix

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

The function Remove_Prefix takes two arguments. The first argument can be a simple string or a list. The second argument is a simple string representing the prefix to remove if present. The prefix is removed from the simple string or from each element of the list.

List := ("libone", "two", "libthree")

Remove_Prefix (List, "lib")

=> ("one", "two", "three")

Remove_Prefix ("libZ.so", "lib")

=> "Z.so"

2.9.6.10. The function Remove_Suffix

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

The function Remove_Suffix takes two arguments. The first argument can be a simple string or a list. The second argument is a simple string representing the suffix to remove if present. The suffix is removed from the simple string or from each element of the list.

List := ("libone", "two", "libthree")

Remove_Suffix (List, "one")

=> ("lib", "two", "libthree")

Remove_Suffix ("libZ.so", ".so")

=> "libZ"

2.9.6.11. The function Split

Function Split takes two single string parameters and return a string list.

Example:

Split ("-gnatf,-gnatv", ",")

=> ("-gnatf", "gnatv")

The first string argument is the string to be split. The second argument is the separator. Each occurrence of the separator in the first argument is a place where it is split. If the first argument is an empty string or contains only occurrences of the separator, then the result is an empty string list. If the argument does not contains any occurrence of the separator, then the result is a list with only one string: the first argument. Empty strings are not included in the result.

Split ("-gnatf   -gnatv", " ")

=> ("-gnatf", "gnatv")

2.9.6.12. The function Upper

Warning

This is not implemented yet in gprbuild or GNATcoll.Projects-based tools such as GNATstudio.

Function Upper takes a single argument which can be a simple string or a list. It returns the argument with all strings in upper case.

Example:

Upper ("The Upper Built-In")

=> "THE UPPER BUILT-IN"

Example with a list:

List := ("One", "Two");

Upper (List)

=> ("ONE", "TWO")

2.9.7. Typed String Declaration

A type declaration introduces a discrete set of string literals. If a string variable is declared to have this type, its value is restricted to the given set of literals. These are the only named types in project files. A type declaration may only appear at the project level, not inside a package.

typed_string_declaration ::=
  'type' <typed_string_>simple_name 'is' ( string_literal {, string_literal} );

The string literals in the list are case sensitive and must all be different. They may include any graphic characters allowed in Ada, including spaces. Here is an example of a string type declaration:

type OS is ("GNU/Linux", "Unix", "Windows", "VMS");

Variables of a string type are called typed variables; all other variables are called untyped variables. Typed variables are particularly useful in case constructions, to support conditional attribute declarations. (See Case Constructions).

A string type may be referenced by its name if it has been declared in the same project file, or by an expanded name whose prefix is the name of the project in which it is declared.

2.9.8. Variables

Variables store values (strings or list of strings) and can appear as part of an expression. The declaration of a variable creates the variable and assigns the value of the expression to it. The name of the variable is available immediately after the assignment symbol, if you need to reuse its old value to compute the new value. Before the completion of its first declaration, the value of a variable defaults to the empty string ("").

A typed variable can be used as part of a case expression to compute the value, but it can only be declared once in the project file, so that all case constructions see the same value for the variable. This provides more consistency and makes the project easier to understand. The syntax for its declaration is identical to the Ada syntax for an object declaration. In effect, a typed variable acts as a constant.

An untyped variable can be declared and overridden multiple times within the same project. It is declared implicitly through an Ada assignment. The first declaration establishes the kind of the variable (string or list of strings) and successive declarations must respect the initial kind. Assignments are executed in the order in which they appear, so the new value replaces the old one and any subsequent reference to the variable uses the new value.

A variable may be declared at the project file level, or within a package.

typed_variable_declaration ::=
  <typed_variable_>simple_name : <typed_string_>name := string_expression;

variable_declaration ::= <variable_>simple_name := expression;

Here are some examples of variable declarations:

This_OS : OS := external ("OS"); --  a typed variable declaration
That_OS := "GNU/Linux";          --  an untyped variable declaration

Name      := "readme.txt";
Save_Name := Name & ".saved";

Empty_List := ();
List_With_One_Element := ("-gnaty");
List_With_Two_Elements := List_With_One_Element & "-gnatg";
Long_List := ("main.ada", "pack1_.ada", "pack1.ada", "pack2_.ada");

A variable reference may take several forms:

  • The simple variable name, for a variable in the current package (if any) or in the current project

  • An expanded name, whose prefix is a context name.

A context may be one of the following:

  • The name of an existing package in the current project

  • The name of an imported project of the current project

  • The name of a direct or indirect base project (i.e., a project extended by the current project, either directly or indirectly)

  • An expanded name whose prefix is an imported/parent project name, and whose selector is a package name in that project.

2.9.9. Case Constructions

A case construction is used in a project file to effect conditional behavior. Through this construction, you can set the value of attributes and variables depending on the value previously assigned to a typed variable.

All choices in a choice list must be distinct. Unlike Ada, the choice lists of all alternatives do not need to include all values of the type. An others choice must appear last in the list of alternatives.

The syntax of a case construction is based on the Ada case construction (although the null declaration for empty alternatives is optional).

The case expression must be a string variable, either typed or not, whose value is often given by an external reference (see The function External).

Each alternative starts with the reserved word when, either a list of literal strings separated by the "|" character or the reserved word others, and the "=>" token. When the case expression is a typed string variable, each literal string must belong to the string type that is the type of the case variable. After each =>, there are zero or more declarations. The only declarations allowed in a case construction are other case constructions, attribute declarations, and variable declarations. String type declarations and package declarations are not allowed. Variable declarations are restricted to variables that have already been declared before the case construction.

case_construction ::=
  'case' <variable_>name 'is' {case_item} 'end' 'case' ;

case_item ::=
  'when' discrete_choice_list =>
    {case_declaration
      | attribute_declaration
      | variable_declaration
      | empty_declaration}

discrete_choice_list ::= string_literal {| string_literal} | 'others'

Here is a typical example, with a typed string variable:

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;