Language Atlas (mini-courses for more languages) Module 5 – Ada with Alire and GNAT
Ada with Alire and GNAT 16: install and build
Install Alire 2.1.1, select GNAT 16.1.0 and GPRbuild, build a first Ada program, compare the three build profiles and see where Ada is used.
What you will learn
- Install Alire 2.1.1, select GNAT 16.1.0 and GPRbuild 26.0.1, then build and run a program with alr
- Describe what alire.toml, the project file and the generated configuration file each hold
- Choose a build profile and predict which checks and warnings a build gets from it
- Fix the warnings and style messages GNAT reports, and predict when they stop a build
- Switch a crate to Ada 2022 and explain where Ada is used and what the licence asks of your programs
Before you start
What you’ll learn · Before you start
What you will learn
- Install Alire 2.1.1, select GNAT 16.1.0 and GPRbuild 26.0.1, then build and run a program with alr
- Describe what alire.toml, the project file and the generated configuration file each hold
- Choose a build profile and predict which checks and warnings a build gets from it
- Fix the warnings and style messages GNAT reports, and predict when they stop a build
- Switch a crate to Ada 2022 and explain where Ada is used and what the licence asks of your programs
Before you start
On this page
A new Ada program that Alire builds is compiled with 31 GNAT switches, most of the compiler’s optional warnings, its validity checks and 22 style checks among them, and still not one assertion is checked: that takes another build profile. Alire 2.1.1 is the package and toolchain manager of Ada, and GNAT 16.1.0 is the compiler of every example in this mini-course, here on a Mac with Apple silicon; both are released for Linux and Windows too.
Here you install Alire, choose the compiler and build a first program. You read the files a new crate is made of, compare what the three build profiles switch on, fix the messages the default profile reports, switch a crate to Ada 2022, and see where Ada is used and what its licence asks of the programs you build.
Alire installs the compiler, then one command builds the crate
Alire publishes its command, alr, as a zip for each platform on its releases page, and for a Mac with Apple silicon
it is alr-2.1.1-bin-aarch64-macos.zip. You put its bin folder on your PATH, and alr then installs everything else
itself: the GNAT compiler and GPRbuild, the tool that drives it, come as crate: In Alire, a folder with an alire.toml manifest: a program or library with a name, a version and its dependencies, built with alr build. The compiler and GPRbuild are crates too. of
Alire’s community index, downloaded into Alire’s own folder rather than into the system. The Alire documentation says
the first alr run offers to choose a toolchain for you; alr toolchain --select makes the choice explicit, which is
how you pin a version. Step through the whole path, with the two checks that can send you back:
The drawing shows the whole scene; its 9 steps are listed below.
Every step as text
- Download the release. Alire publishes alr as a zip per platform on its GitHub releases page. For a Mac with Apple silicon it is alr-2.1.1-bin-aarch64-macos.zip, 8,816,840 bytes, holding bin/alr and the licence.
- Check the file. shasum -a 256 prints the SHA-256 of the zip. The release lists 2c4867bf… for this file, and that is the zip this lesson's runs used; a different hash means a different or damaged file.
- Hash differs. A mismatch sends you back to the download. Run nothing from a file whose hash does not match.
- Put alr on your PATH. Unzip it into a folder you own and add its bin folder to PATH. macOS may refuse to run a downloaded binary; the Alire documentation removes the quarantine with xattr -d com.apple.quarantine bin/alr. alr --version must then say alr 2.1.1.
- Another alr first. An older alr earlier on your PATH answers instead, and its index and switches differ. Fix the PATH before you compare any output with this lesson.
- Select the toolchain. alr toolchain --select gnat_native=16.1.0 gprbuild=26.0.1 downloads the GNAT FSF compiler, a 146,705,293-byte archive for this Mac, and GPRbuild into Alire's own folder, and makes them the default for every crate. Nothing else on your system changes.
- Make a crate. alr -n init --bin hello creates the folder hello with alire.toml, hello.gpr and src/hello.adb, answering its questions with their defaults.
- Build. alr build writes config/hello_config.gpr with the 31 switches of the development profile, then calls GPRbuild, which compiles, binds and links the program into bin/hello.
- Run. alr run checks that the build is up to date and starts bin/hello, which printed Hello from Ada, Compiled by GNAT 16.1.0 and Integer'Last = 2147483647.
The recorded session starts with Alire installed and alr toolchain --select gnat_native=16.1.0 gprbuild=26.0.1
already run. Its new crate holds an empty main program, which the session replaces with this one; the package
GNAT.Compiler_Version reports which compiler built it:
with Ada.Text_IO;
with GNAT.Compiler_Version;
procedure Hello is
package Compiler is new GNAT.Compiler_Version;
begin
Ada.Text_IO.Put_Line ("Hello from Ada");
Ada.Text_IO.Put_Line ("Compiled by GNAT " & Compiler.Version);
Ada.Text_IO.Put_Line ("Integer'Last =" & Integer'Last'Image);
end Hello; from session import run
# The first session of the Ada mini-course, with Alire 2.1.1 installed and its toolchain selected: the versions,
# a new crate made by alr init, the files it wrote, a first build and run, the switches the build used, and the
# compiler's own version. hello.adb replaces the empty main program that alr init writes.
run("""
alr --version
alr toolchain
alr -n init --bin hello
cd hello
find . -type f -not -path './alire/*' | sort
cat src/hello.adb
cp ../hello.adb src/hello.adb
alr build
alr run
grep -o '"-[^"]*"' config/hello_config.gpr | wc -l
alr exec -- gnat --version
""") Output
$ alr --version
alr 2.1.1
$ alr toolchain
CRATE VERSION STATUS NOTES
gprbuild 26.0.1 Default
gnat_native 16.1.0 Default
$ alr -n init --bin hello
Enter a short description of the crate: (default: '')
Using default: ''
Select a software license for the crate?
Using default choice in non-interactive mode: MIT OR Apache-2.0 WITH LLVM-exception
Warning: TRUE
Enter a comma (',') separated list of tags to help people find your crate: (default: '')
Using default: ''
Enter an optional Website URL for the crate: (default: '')
Using default: ''
Success: hello initialized successfully.
$ cd hello
$ find . -type f -not -path './alire/*' | sort
./.gitignore
./alire.toml
./config/hello_config.ads
./config/hello_config.gpr
./config/hello_config.h
./hello.gpr
./src/hello.adb
$ cat src/hello.adb
procedure Hello is
begin
null;
end Hello;
$ cp ../hello.adb src/hello.adb
$ alr build
Note: Building hello=0.1.0-dev/hello.gpr...
Setup
[mkdir] object directory for project Hello
[mkdir] exec directory for project Hello
Compile
[Ada] hello.adb
Bind
[gprbind] hello.bexch
[Ada] hello.ali
Link
[link] hello.adb
Success: Build finished successfully in … seconds.
$ alr run
Note: Building hello=0.1.0-dev/hello.gpr...
gprbuild: "hello" up to date
Success: Build finished successfully in … seconds.
Hello from Ada
Compiled by GNAT 16.1.0
Integer'Last = 2147483647
$ grep -o '"-[^"]*"' config/hello_config.gpr | wc -l
31
$ alr exec -- gnat --version
GNAT 16.1.0
Copyright (C) 1996-2025, Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 setup_session.py
Read it from the top. alr toolchain lists the two tools the crate builds with, gnat_native 16.1.0 and gprbuild
26.0.1, both marked Default. alr -n init --bin hello makes the crate: -n answers every question with its default,
so the description and website stay empty and the licence becomes MIT OR Apache-2.0 WITH LLVM-exception, which you
can change later. The odd line Warning: TRUE comes from alr’s own prompt library, which prints whether its output
is a terminal when it answers a choice for you; it changes nothing.
The build runs in four phases: Setup makes the object and program folders, Compile runs GNAT on hello.adb, Bind
checks that all units fit together and writes their start-up code, and Link makes the program. alr run rebuilds
only if a file changed, and starts it. The program printed what built it, GNAT 16.1.0, and Integer'Last with a
space in front of 2147483647: the attribute 'Image gives a non-negative number a leading space, a rule lesson 2
comes back to. alr exec -- gnat --version runs a command with the crate’s toolchain on the PATH.
Repeat the first build
With Alire 2.1.1 installed and the toolchain selected, type alr -n init --bin hello, cd hello, then alr run.
The empty main program builds and prints nothing; put the lesson’s hello.adb in src and run again to see the
compiler’s version.
Checkpoint
Check: Installing Alire and building a first crate
alire.toml describes the crate, and alr writes the switches
A crate is a folder with a manifest, alire.toml, that names it and the programs it builds. Beside it, alr init
wrote hello.gpr, a GPR project file: A .gpr file read by GPRbuild: the source folders, the main programs, the folders for objects and programs, and the compiler switches of an Ada build.: where the sources are, which file is the main
program, and where objects and programs go. The third file below was not written by alr init but by alr build:
project/alire.toml
name = "hello"
description = ""
version = "0.1.0-dev"
authors = ["Learner"]
maintainers = ["Learner <learner@example.com>"]
maintainers-logins = ["learner"]
licenses = "MIT OR Apache-2.0 WITH LLVM-exception"
website = ""
tags = []
executables = ["hello"] project/hello.gpr
with "config/hello_config.gpr";
project Hello is
for Source_Dirs use ("src/", "config/");
for Object_Dir use "obj/" & Hello_Config.Build_Profile;
for Create_Missing_Dirs use "True";
for Exec_Dir use "bin";
for Main use ("hello.adb");
package Compiler is
for Default_Switches ("Ada") use Hello_Config.Ada_Compiler_Switches;
end Compiler;
package Binder is
for Switches ("Ada") use ("-Es"); -- Symbolic traceback
end Binder;
package Install is
for Artifacts (".") use ("share");
end Install;
end Hello; config/hello_config.gpr · project/hello_config.gpr
-- Configuration for hello generated by Alire
abstract project Hello_Config is
Crate_Version := "0.1.0-dev";
Crate_Name := "hello";
Alire_Host_OS := "macos";
Alire_Host_Arch := "aarch64";
Alire_Host_Distro := "distribution_unknown";
Ada_Compiler_Switches := External_As_List ("ADAFLAGS", " ");
Ada_Compiler_Switches := Ada_Compiler_Switches &
(
"-Og" -- Optimize for debug
,"-ffunction-sections" -- Separate ELF section for each function
,"-fdata-sections" -- Separate ELF section for each variable
,"-g" -- Generate debug info
,"-gnatwa" -- Enable all warnings
,"-gnatw.X" -- Disable warnings for No_Exception_Propagation
,"-gnatVa" -- All validity checks
,"-gnaty3" -- Specify indentation level of 3
,"-gnatya" -- Check attribute casing
,"-gnatyA" -- Use of array index numbers in array attributes
,"-gnatyB" -- Check Boolean operators
,"-gnatyb" -- Blanks not allowed at statement end
,"-gnatyc" -- Check comments
,"-gnaty-d" -- Disable check no DOS line terminators present
,"-gnatye" -- Check end/exit labels
,"-gnatyf" -- No form feeds or vertical tabs
,"-gnatyh" -- No horizontal tabs
,"-gnatyi" -- Check if-then layout
,"-gnatyI" -- check mode IN keywords
,"-gnatyk" -- Check keyword casing
,"-gnatyl" -- Check layout
,"-gnatym" -- Check maximum line length
,"-gnatyn" -- Check casing of entities in Standard
,"-gnatyO" -- Check that overriding subprograms are explicitly marked as such
,"-gnatyp" -- Check pragma casing
,"-gnatyr" -- Check identifier references casing
,"-gnatyS" -- Check no statements after THEN/ELSE
,"-gnatyt" -- Check token spacing
,"-gnatyu" -- Check unnecessary blank lines
,"-gnatyx" -- Check extra parentheses
,"-gnatW8" -- UTF-8 encoding for wide characters
);
type Build_Profile_Kind is ("release", "validation", "development");
Build_Profile : Build_Profile_Kind := "development";
end Hello_Config; The project file’s first line, with "config/hello_config.gpr";, pulls in the generated file, and its Compiler
package uses that file’s Ada_Compiler_Switches, so the switches never live in hello.gpr itself. The object folder
is obj/ plus the profile’s name, obj/development here, which keeps the objects of different profiles apart. Step
through one build and watch who reads what:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The manifest. alr build starts by reading alire.toml, the crate's manifest: its name, version, the programs it builds and, later in this lesson, its build switches.
- alr writes the configuration. From the manifest and the build profile, alr writes config/hello_config.gpr, with the list of compiler switches, and config/hello_config.ads. You never edit them: alr writes them again on every build.
- The project file. hello.gpr is the GPRbuild project: where the sources are, which file is the main program, where objects and programs go. Its first line, with "config/hello_config.gpr", pulls in the switches alr wrote.
- GPRbuild takes over. alr calls GPRbuild with -P hello.gpr. GPRbuild works out what needs compiling and in which order; it is the build tool, and alr only prepares its input.
- GNAT compiles. GPRbuild runs GNAT on src/hello.adb with the switches from the configuration file, then binds and links the objects with the Ada run-time library.
- The program. The program lands in bin/hello, because hello.gpr says Exec_Dir is bin. alr run starts that file, and you can start it yourself as ./bin/hello.
Two habits follow from it. Change the crate in alire.toml and hello.gpr, never in config/, because alr build
writes that folder again every time; the .gitignore that alr init wrote already keeps config/, obj/, bin/
and alire/ out of version control. And add libraries with alr with <crate>, which records the dependency in
alire.toml and the exact versions it chose in alire/alire.lock.
Key idea
You edit two files, alire.toml for the crate and hello.gpr for the build; alr writes the switches into
config/ on every build, so they follow the profile instead of being copied around.
Checkpoint
Check: What alire.toml, the project file and config/ hold
The build profile decides which checks the compiler runs
Alire has three build profile: One of Alire's three named sets of compiler switches for a crate: development (the default), validation or release, chosen with alr build --validation or --release., and its documentation says the crate you are working
on builds in development unless you ask for another one, while its dependencies build in release. alr build --validation and alr build --release choose the others. This session builds the same new crate with both and
prints the switches each build used:
from session import run
# The same new crate built with the two other build profiles: alr writes the switches of the profile into
# config/hello_config.gpr before it calls GPRbuild, so each list below is what that build compiled with.
run("""
alr -n init --bin hello
cd hello
alr build --validation
grep -o '"-[^"]*"' config/hello_config.gpr | tr '\\n' ' '
alr build --release
grep -o '"-[^"]*"' config/hello_config.gpr | tr '\\n' ' '
""") Output
$ alr -n init --bin hello
Enter a short description of the crate: (default: '')
Using default: ''
Select a software license for the crate?
Using default choice in non-interactive mode: MIT OR Apache-2.0 WITH LLVM-exception
Warning: TRUE
Enter a comma (',') separated list of tags to help people find your crate: (default: '')
Using default: ''
Enter an optional Website URL for the crate: (default: '')
Using default: ''
Success: hello initialized successfully.
$ cd hello
$ alr build --validation
Note: Building hello=0.1.0-dev/hello.gpr...
Setup
[mkdir] object directory for project Hello
[mkdir] exec directory for project Hello
Compile
[Ada] hello.adb
Bind
[gprbind] hello.bexch
[Ada] hello.ali
Link
[link] hello.adb
Success: Build finished successfully in … seconds.
$ grep -o '"-[^"]*"' config/hello_config.gpr | tr '\n' ' '
"-O3" "-gnatn" "-ffunction-sections" "-fdata-sections" "-g" "-gnato" "-gnatwa" "-gnatw.X" "-gnatVa" "-gnatwe" "-gnata" "-gnaty3" "-gnatya" "-gnatyA" "-gnatyB" "-gnatyb" "-gnatyc" "-gnaty-d" "-gnatye" "-gnatyf" "-gnatyh" "-gnatyi" "-gnatyI" "-gnatyk" "-gnatyl" "-gnatym" "-gnatyn" "-gnatyO" "-gnatyp" "-gnatyr" "-gnatyS" "-gnatyt" "-gnatyu" "-gnatyx" "-gnatW8"
$ alr build --release
Note: Building hello=0.1.0-dev/hello.gpr...
Setup
[mkdir] object directory for project Hello
Compile
[Ada] hello.adb
Bind
[gprbind] hello.bexch
[Ada] hello.ali
Link
[link] hello.adb
Success: Build finished successfully in … seconds.
$ grep -o '"-[^"]*"' config/hello_config.gpr | tr '\n' ' '
"-O3" "-gnatn" "-ffunction-sections" "-fdata-sections" "-gnatW8"
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 profiles_session.py
Side by side, with the 31 switches of the first session:
| What the build gets | development | validation | release |
|---|---|---|---|
| optimisation | -Og |
-O3 -gnatn |
-O3 -gnatn |
debug information, -g |
yes | yes | no |
most optional warnings, -gnatwa |
yes | yes | no |
all validity checks, -gnatVa |
yes | yes | no |
22 style checks, -gnaty… |
yes | yes | no |
assertions and contracts, -gnata |
no | yes | no |
warnings and style as errors, -gnatwe |
no | yes | no |
Two rows matter most for the rest of the mini-course. Only validation has -gnata, which the GNAT User’s Guide says
enables assertions, preconditions, postconditions, type invariants and predicates, all disabled by default; the
contracts of lesson 3 are ignored by every other build. And only validation has -gnatwe, so a warning that the
development build merely prints stops a validation build. Validation also adds -gnato explicitly, though the User’s
Guide says overflow checking is already on when no -gnato is given, as lesson 2’s overflow shows.
One profile for each job
Write and debug with development, run your tests and every CI build with alr build --validation, and ship with
--release. The choice does not stick to the crate: a plain alr build builds in development again, while
alr run, which rebuilds before it starts the program, reuses the profile of your last alr build.
Checkpoint
Check: What each build profile switches on
A style message is only a warning until validation makes it an error
GNAT’s style check: A GNAT check, switched on with -gnaty, of how code is written rather than what it does, such as casing, line length or and then in conditions. It reports a (style) message. look at how code is written, not what it does. Here is a first draft of a small program that accepts a payment of up to 100,000 paise. It works, and it has four slips:
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Text_IO; use Ada.Text_IO;
procedure Limits is
Amount : constant Integer := Integer'Value (Argument (1));
Spare : Integer := 0;
begin
if Amount >= 0 and Amount <= 100_000 then
put_line ("Paid:" & Amount'Image & " paise, within the limit of 100000 paise");
end if;
end Limits; The development build reports them and still links the program:
from session import run
# limits.adb as a first draft often looks: it builds with the development profile, which reports every warning and
# style message and still links the program.
run("""
cd limits
alr build
./bin/limits 1205
""") Output
$ cd limits $ alr build Note: Synchronizing workspace... Nothing to update. Note: Building limits=0.1.0-dev/limits.gpr... Setup [mkdir] object directory for project Limits [mkdir] exec directory for project Limits Compile [Ada] limits.adb limits.adb:6:04: warning: variable "Spare" is not referenced [-gnatwu] limits.adb:8:19: (style) "and then" required [-gnatyB] limits.adb:9:07: (style) bad casing of "Put_Line" declared at a-textio.ads:565 [-gnatyr] limits.adb:9:80: (style) this line is too long: 85 [-gnatyM] Bind [gprbind] limits.bexch [Ada] limits.ali Link [link] limits.adb Success: Build finished successfully in … seconds. $ ./bin/limits 1205 Paid: 1205 paise, within the limit of 100000 paise
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 style_session.py
Each message names its place, line and column, and ends with the switch that produced it. The warning
variable "Spare" is not referenced comes from -gnatwu, part of -gnatwa. The three (style) messages come from
the -gnaty checks: "and then" required because the User’s Guide’s rule -gnatyB permits and and or only for
modular or array operands and simple Boolean variables or constants, so a condition built from comparisons needs the
Ada's short-circuit forms: and then evaluates its right side only when the left side is True, or else only when it is False, so the right side may rely on the left.;
bad casing of "Put_Line" because -gnatyr wants every use of a name spelt as it was declared, here at line 565 of
a-textio.ads, the specification of Ada.Text_IO; and this line is too long: 85 because -gnatym allows 79
characters, a check the message tags with -gnatyM, the switch that sets another length. The same draft with the
validation profile, then the fixed file:
from session import run
# The same draft with the validation profile, which treats warnings and style messages as errors; then the fixed
# file from limits-fixed, built the same way.
run("""
cd limits
!alr build --validation
cp ../limits-fixed/limits.adb src/limits.adb
alr build --validation
./bin/limits 1205
""") Output
$ cd limits $ alr build --validation Note: Synchronizing workspace... Nothing to update. Note: Building limits=0.1.0-dev/limits.gpr... Setup [mkdir] object directory for project Limits [mkdir] exec directory for project Limits Compile [Ada] limits.adb limits.adb:6:04: warning: variable "Spare" is not referenced [-gnatwu] limits.adb:8:19: (style) "and then" required [-gnatyB] limits.adb:9:07: (style) bad casing of "Put_Line" declared at a-textio.ads:565 [-gnatyr] limits.adb:9:80: (style) this line is too long: 85 [-gnatyM] compilation of limits.adb failed gprbuild: *** compilation phase failed ERROR: Command ["gprbuild", "-s", "-j0", "-p", "-P", "limits/limits.gpr"] exited with code 4 ERROR: Compilation failed. [exit status 1] $ cp ../limits-fixed/limits.adb src/limits.adb $ alr build --validation Note: Building limits=0.1.0-dev/limits.gpr... Compile [Ada] limits.adb Bind [gprbind] limits.bexch [Ada] limits.ali Link [link] limits.adb Success: Build finished successfully in … seconds. $ ./bin/limits 1205 Paid: 1205 paise, within the limit of 100000 paise
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 strict_session.py
Under validation the very same four messages stopped the build, because -gnatwe counts them as errors: compilation of limits.adb failed, exit status 1 from alr. The fixed file removed the unused variable, wrote and then, used
the declared casing and split the long line, and it built with no message at all.
Switching the checks off to make CI pass
Removing -gnatwe or setting "*".style_checks = "No" in alire.toml turns a red validation build green and
keeps every slip. Fix the messages instead: they are short, the switch in brackets names the rule, and the rule’s
entry in the GNAT User’s Guide says exactly what it wants.
Checkpoint
Check: Reading warnings and style messages
GNAT 16 compiles Ada 2012 until a crate asks for Ada 2022
The GNAT User’s Guide says the compiler’s default mode is Ada 2012, and GNAT 16.1.0 still keeps to it: forms that are
new in Ada 2022, such as the two below, do not compile until a switch asks for that edition. This program counts the
passes in a class with an aggregate in square brackets and @, which stands for the target of an assignment:
with Ada.Text_IO; use Ada.Text_IO;
procedure Brackets is
type Marks is array (1 .. 4) of Natural;
Class : constant Marks := [72, 95, 38, 48];
Passed : Natural := 0;
begin
for Mark of Class loop
if Mark >= 40 then
Passed := @ + 1;
end if;
end loop;
Put_Line ("Passed:" & Passed'Image & " of" & Class'Length'Image);
end Brackets; from session import run
# brackets.adb uses two Ada 2022 forms, a [ ] aggregate and @ for the target of an assignment. GNAT 16 compiles
# Ada 2012 unless a switch asks for 2022, so the first build stops; the second alire.toml asks for Ada 2022, and the
# switch it adds is then in the generated configuration.
run("""
cd brackets
!alr build
cp ../brackets-2022/alire.toml alire.toml
tail -2 alire.toml
alr build
grep -o '"-gnat2022"' config/brackets_config.gpr
./bin/brackets
""") Output
$ cd brackets
$ alr build
Note: Synchronizing workspace...
Nothing to update.
Note: Building brackets=0.1.0-dev/brackets.gpr...
Setup
[mkdir] object directory for project Brackets
[mkdir] exec directory for project Brackets
Compile
[Ada] brackets.adb
brackets.adb:5:31: error: illegal character, replaced by "("
brackets.adb:10:20: error: target name is an Ada 2022 feature
brackets.adb:10:20: error: unit must be compiled with -gnat2022 switch
compilation of brackets.adb failed
gprbuild: *** compilation phase failed
ERROR: Command ["gprbuild", "-s", "-j0", "-p", "-P", "brackets/brackets.gpr"] exited with code 4
ERROR: Compilation failed.
[exit status 1]
$ cp ../brackets-2022/alire.toml alire.toml
$ tail -2 alire.toml
[build-switches]
"*".ada_version = "Ada2022"
$ alr build
Note: Synchronizing workspace...
Nothing to update.
Note: Building brackets=0.1.0-dev/brackets.gpr...
Compile
[Ada] brackets.adb
Bind
[gprbind] brackets.bexch
[Ada] brackets.ali
Link
[link] brackets.adb
Success: Build finished successfully in … seconds.
$ grep -o '"-gnat2022"' config/brackets_config.gpr
"-gnat2022"
$ ./bin/brackets
Passed: 3 of 4
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 ada2022_session.py
The first build read [ as an illegal character and said outright that the target name @ is an Ada 2022 feature
and the unit must be compiled with -gnat2022. Two lines in alire.toml fixed it: the [build-switches] table
with "*".ada_version = "Ada2022", where * means every profile. Alire then adds -gnat2022 to the generated
switches, and the program counted 3 passes of 4. Every crate in this mini-course carries those two lines.
Ada 2022 is the fourth edition of the standard, ISO/IEC 8652:2023, and the language specification: The document that defines what a language means for every implementation, often an international standard, such as the Ada Reference Manual (ISO/IEC 8652:2023) or the C standard. More in “Reading language references and release notes” every example of this mini-course follows is its Reference Manual. Walk through the editions to see where the features of the coming lessons came from:
- Ada 83
The first edition
The Reference Manual names three overriding concerns of the original design: program reliability and maintenance, programming as a human activity, and efficiency. Readability was put before ease of writing.
- Ada 95
The 1995 revision protected objects
The revision added flexibility and extensibility, more control over storage management and synchronization, and standard packages for important application areas. Protected objects, which share data between tasks, are new in it.
- Ada 2005
Amendment 1 interfaces
Interface types arrived, multiple inheritance of interfaces in the manner of Java, together with the standard containers library.
- Ada 2012
Contracts in the language contracts
Pre and Post aspects, subtype predicates and type invariants became part of the language, with expression functions, quantified expressions and for … of loops.
- Ada 2022
ISO/IEC 8652:2023 [ ] aggregates
The fourth edition adds parallel loops and blocks, the Global aspect, delta aggregates, the @ shorthand, declare expressions, reduction expressions and an Image attribute for every type. GNAT 16 compiles it with -gnat2022.
The timeline has 5 events, from Ada 83 to Ada 2022.
The Reference Manual marks its history in its paragraph numbers, which helps when you read it next to an older book:
a number such as 8/3 changed in the Ada 2012 edition and 1/5 in Ada 2022, while an unmarked number is unchanged
since 1995.
Checkpoint
Check: GNAT's default Ada 2012 and the switch to Ada 2022
Where Ada is used, and what GNAT’s licence asks of your programs
Ada is a language for software that must keep working. AdaCore, which contributes to GNAT and makes the SPARK tools, lists the industries its tools serve as avionics, defence, automotive, rail, medical, space and security. Its SPARK User’s Guide describes SPARK as a language and a set of verification tools for high-assurance software, based on Ada with the features that defy verification removed, and names air-traffic management and avionics among the systems it suits; lesson 3 runs its prover on a small program. In the Stack Overflow Developer Survey 2025, about 1.4 % of all respondents and 3 % of those learning to code said they had done extensive development work in Ada in the past year. Those are small numbers next to C or Java, and they are why this mini-course spends its time on what makes Ada different rather than on syntax.
The licences are in the crates themselves. Alire’s index lists the gnat_native 16.1.0 compiler as
GPL-3.0-or-later AND GPL-3.0-or-later WITH GCC-exception-3.1: the compiler is under the GPL, and the run-time
library that every program links, Ada.Text_IO among it, carries the GCC Runtime Library Exception 3.1, as the
header of a-textio.ads states. Three licences meet in a program you build:
| Part of the program | Licence | What it asks of you |
|---|---|---|
| The GNAT compiler | GPL-3.0-or-later | nothing for using it; the GPL’s conditions apply if you pass the compiler on |
| The run-time library it links in | GPL-3.0-or-later with the GCC Runtime Library Exception 3.1 | nothing for code compiled by GNAT |
| Your own crate | licences in alire.toml, MIT OR Apache-2.0 WITH LLVM-exception after alr -n init |
your choice |
The exception is what makes the middle row harmless: it lets you combine the run-time library with your own code, compiled by an eligible process such as GNAT, and convey the result on terms of your choice, so a program you build need not be GPL.
Organisations named on this page
AdaCore, the Alire project, the Free Software Foundation and Stack Overflow appear here because they make the tools, publish the licence texts or ran the survey this page cites. MySmartCoPilot is not connected with any of them; the runs on this page used the GNAT compiler as Alire installs it.
Checkpoint
Check: Where Ada is used and what the licence asks
Practise: read a build log the way the compiler means it
The exercise turns the messages of this lesson into code: split a line of a build log into its file, line, column,
kind, text and switch, then say whether a build with a given profile stops. Its sample tests replay the lesson’s own
logs, the four messages about limits.adb and the three errors about brackets.adb.
Exercise · Easy · Python
Read GNAT's messages and say whether a build fails
A build log from alr build mixes progress lines with the compiler's messages. Before you fix anything, you want to know which lines are messages, what kind each one is, and whether the build stops. Write two functions in messages.py.
parse(line) reads one line of the log. When it is a GNAT message, it returns a dictionary with six keys:
"file","line"and"column": the place, as inlimits.adb:8:19:(the numbers asint)."kind":"error"when the text after the place starts witherror:,"warning"when it starts withwarning:, and"style"when it starts with(style)."text": the message itself, without that prefix and without the switch at the end."switch": what the square brackets at the very end hold, such as"-gnatyB"or"enabled by default", orNonewhen the line ends without them.
Any other line, such as Compile, [Ada] limits.adb or Success: Build finished successfully in … seconds., gives None.
build_fails(profile, lines) takes a profile name, "development", "validation" or "release", and the lines of one build log. It returns True when the build stops: an error stops every build, and the validation profile also treats every warning and style message as an error, because it compiles with -gnatwe. Otherwise it returns False.
The sample tests use the lesson's own logs: the four messages about limits.adb, and the three errors about brackets.adb in Ada 2012 mode.
Starter code · messages.py
def parse(line):
"""The parts of one GNAT message (file, line, column, kind, text, switch), or None for any other line."""
# Replace this line with your code.
return None
def build_fails(profile, lines):
"""Whether a build with this profile ("development", "validation" or "release") stops on these log lines."""
# Replace this line with your code.
return False The sample tests · test_messages.py
from messages import build_fails, parse
LIMITS = [
"Compile",
" [Ada] limits.adb",
'limits.adb:6:04: warning: variable "Spare" is not referenced [-gnatwu]',
'limits.adb:8:19: (style) "and then" required [-gnatyB]',
'limits.adb:9:07: (style) bad casing of "Put_Line" declared at a-textio.ads:565 [-gnatyr]',
"limits.adb:9:80: (style) this line is too long: 85 [-gnatyM]",
"Bind",
]
BRACKETS = [
'brackets.adb:5:31: error: illegal character, replaced by "("',
"brackets.adb:10:20: error: target name is an Ada 2022 feature",
"brackets.adb:10:20: error: unit must be compiled with -gnat2022 switch",
]
def test_a_warning():
"""The unused variable: a warning, with the switch that reports it"""
assert parse(LIMITS[2]) == {"file": "limits.adb", "line": 6, "column": 4, "kind": "warning", "text": 'variable "Spare" is not referenced', "switch": "-gnatwu"}
def test_style_messages():
"""The three style messages, one of them with a colon inside its text"""
assert parse(LIMITS[3])["kind"] == "style"
assert parse(LIMITS[3])["text"] == '"and then" required'
assert parse(LIMITS[4])["text"] == 'bad casing of "Put_Line" declared at a-textio.ads:565'
assert parse(LIMITS[4])["switch"] == "-gnatyr"
assert parse(LIMITS[5]) == {"file": "limits.adb", "line": 9, "column": 80, "kind": "style", "text": "this line is too long: 85", "switch": "-gnatyM"}
def test_an_error_without_a_switch():
"""An error from the Ada 2012 build: no brackets at the end"""
assert parse(BRACKETS[0]) == {"file": "brackets.adb", "line": 5, "column": 31, "kind": "error", "text": 'illegal character, replaced by "("', "switch": None}
assert parse(BRACKETS[2])["text"] == "unit must be compiled with -gnat2022 switch"
def test_a_switch_in_words():
"""The warning about a value out of range ends with [enabled by default]"""
m = parse('marks.adb:11:35: warning: Constraint_Error will be raised at run time [enabled by default]')
assert m["kind"] == "warning"
assert m["switch"] == "enabled by default"
assert m["text"] == "Constraint_Error will be raised at run time"
def test_brackets_inside_the_text():
"""A message that ends with ] but has no switch keeps its whole text"""
m = parse("scores.adb:3:10: error: no index 5 in Scores[1 .. 4]")
assert m["text"] == "no index 5 in Scores[1 .. 4]"
assert m["switch"] is None
def test_other_lines():
"""Progress lines and the last line of a build are not messages"""
assert parse("Compile") is None
assert parse(" [Ada] limits.adb") is None
assert parse("Success: Build finished successfully in … seconds.") is None
assert parse("") is None
def test_the_profiles():
"""The draft builds with development and stops with validation; an error stops every profile"""
assert build_fails("development", LIMITS) is False
assert build_fails("validation", LIMITS) is True
assert build_fails("release", LIMITS) is False
assert build_fails("development", BRACKETS) is True
assert build_fails("release", BRACKETS) is True
assert build_fails("validation", ["Compile", "Bind", "Link"]) is False A hint
Split the line at the first three colons only: the file, the line and the column come first, and the text after them may hold colons of its own, as in bad casing of "Put_Line" declared at a-textio.ads:565. Then look at how the rest starts, and take the switch off the end only when the line ends with ].
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then test yourself on the six ideas of this lesson, from installing alr to the licences. Your weakest idea is asked about first, and a wrong pick names the slip behind it:
Practice that adapts to you
Practise this lesson
13 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to mid-level): What are Alire, GNAT and GPRbuild, and which one does alr build run?
GNAT is the Ada compiler, part of the GNU Compiler Collection. GPRbuild is the build tool that reads a .gpr project
file and runs the compiler, the binder and the linker on the right files in the right order. Alire is Ada’s package
and toolchain manager: its alr command installs GNAT and GPRbuild as crates, keeps a crate’s dependencies in
alire.toml, and writes the compiler switches of the chosen build profile into config/. alr build runs all
three in turn: alr prepares the configuration, then calls GPRbuild with the crate’s project file, and GPRbuild runs
GNAT. You can still call gprbuild yourself, through alr exec --, with the same project file.
Two questions an Ada team might put to a new colleague who will run its builds:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to senior
An Ada crate builds on every developer's machine with alr build, but the CI job, which runs alr build --validation, fails with a page of style messages and one warning. What is going on, and how do you get the team back to green without weakening CI?
-
Mid to staff
A new Ada project must build reproducibly for an audit: the same compiler and build tool on every laptop and in CI, for years. How do you set that up with Alire, and what do you check on every build?
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
How do you pin the compiler and the build tool that Alire uses?
alr toolchain --select gnat_native=16.1.0 gprbuild=26.0.1. GNAT and GPRbuild are crates of Alire's index, so they install into Alire's own folder instead of the system.
-
Which files of an Alire crate do you edit, and which one does alr rewrite?
You edit alire.toml and the crate's .gpr project file. alr writes the config/ folder afresh on every build, with the switches of the current profile, so changes there are lost.
-
Which Alire build profile turns on assertions and contracts?
Only validation: it adds -gnata, and also -gnatwe, which makes every warning and style message an error. The development and release profiles leave contracts unchecked.
-
In which profile does a plain alr build compile your crate, and its dependencies?
Your crate in development, unless you pass --validation or --release; the crates it depends on in release.
-
Why does GNAT's style check ask for and then in a condition built from two comparisons?
Rule -gnatyB allows plain and and or only for modular or array operands and simple Boolean variables or constants. Comparisons joined in a condition take the short-circuit form.
-
Which Ada edition does GNAT 16.1.0 compile by default, and how does a crate move to Ada 2022?
Ada 2012. Two lines in alire.toml, a [build-switches] table with "*".ada_version = "Ada2022", make Alire add -gnat2022 to every profile.
-
Does a program built with GNAT have to be released under the GPL?
No. The run-time library it links carries the GCC Runtime Library Exception 3.1, so code compiled with GNAT may be passed on under terms of your choice.
-
A validation build fails on style messages. Why is switching the style checks off the wrong fix?
It keeps every slip in the code. Each message gives its line, its column and the switch behind it, and that switch's entry in the GNAT User's Guide says exactly what it wants.
Key takeaways
- Alire 2.1.1 is installed from one zip, and
alr toolchain --select gnat_native=16.1.0 gprbuild=26.0.1pins the compiler and the build tool;alr init,alr buildandalr runthen make, build and start a crate. - A crate is
alire.tomlplus a GPR project file; alr writes the compiler switches intoconfig/on every build, so you never edit that folder. - The development profile compiled with 31 switches, warnings and style checks among them, but only validation
enables assertions with
-gnataand turns warnings into errors with-gnatwe; release keeps optimisation only. - GNAT’s messages name their place and the switch that produced them, and the same four messages that the development build printed stopped the validation build.
- GNAT 16 compiles Ada 2012 by default, so every crate here sets
"*".ada_version = "Ada2022"; the compiler is under the GPL, and the run-time library’s exception leaves your program’s licence to you.
Terms in this lesson
- build profile
- One of Alire's three named sets of compiler switches for a crate: development (the default), validation or release, chosen with alr build --validation or --release.
- crate
- In Alire, a folder with an alire.toml manifest: a program or library with a name, a version and its dependencies, built with alr build. The compiler and GPRbuild are crates too.
- GPR project file
- A .gpr file read by GPRbuild: the source folders, the main programs, the folders for objects and programs, and the compiler switches of an Ada build.
- style check
- A GNAT check, switched on with -gnaty, of how code is written rather than what it does, such as casing, line length or and then in conditions. It reports a (style) message.
References
- Alire: the Ada library manager (Alire project)
- Alire documentation (getting started, build profiles and switches, settings) (Alire project)
- Alire release v2.1.1 (binaries and their SHA-256) (Alire project)
- GNAT FSF builds: release gnat-16.1.0-1 (Alire project)
- Alire community index: the gnat_native 16.1.0 manifest (licences) (Alire project)
- GNAT User's Guide: Building executable programs with GNAT (switches, style checking, language version) (AdaCore)
- Ada Reference Manual, Ada 2022: contents (Ada Information Clearinghouse)
- Ada Reference Manual, Ada 2022: Foreword (Ada Information Clearinghouse)
- Ada Reference Manual, Ada 2022: Introduction (design goals, paragraph numbers) (Ada Information Clearinghouse)
- Annotated Ada Reference Manual 9.4: Protected Units and Protected Objects (Ada Information Clearinghouse)
- Annotated Ada Reference Manual 3.9.4: Interface Types (Ada Information Clearinghouse)
- Annotated Ada Reference Manual 6.1.1: Preconditions and Postconditions (Ada Information Clearinghouse)
- Annotated Ada Reference Manual 4.5.10: Reduction Expressions (Ada Information Clearinghouse)
- SPARK User's Guide: Introduction (AdaCore)
- AdaCore: industries (AdaCore)
- GCC Runtime Library Exception, version 3.1 (Free Software Foundation)
- Stack Overflow Developer Survey 2025: Technology (Stack Overflow)
- clic, the command-line library of alr, at the commit alr 2.1.1 builds with: clic-user_input.adb (Alire project)
Report a problem with this lesson
Kept only in this browser. Your Learn progress