Build Tools: GNAT, GPR Project Files & Alire

Understand the GNAT toolchain (gcc, gnatbind, gnatlink, gnatmake, gnatls, gnatclean, gnatprep), GPR project files (Source_Dirs, Object_Dir, Main, scenario variables, Compiler/Binder/Linker packages, library projects), gprbuild, and the Alire package manager (alr init, with, build, run, crates and toolchains).

A single-file Ada program only needs gnatmake. Real projects use GPR project files built by gprbuild, and the Alire package manager, which downloads toolchains and library crates much like Cargo or npm. This lesson shows each layer and which tool does what.

The GNAT Toolchain

Building an Ada program has three steps: compile each unit (gcc -c produces .o and .ali files; .ali files record dependencies and compile options), bind (gnatbind checks the consistency of the whole program and generates elaboration code in b~main.adb), and link (gnatlink). gnatmake runs all three and recompiles only what changed. Other tools: gnatls (lists units and search paths), gnatclean (removes build products), gnatprep (a preprocessor for conditional code), gnatchop (splits files containing several units), gdb (debugger) and gprof/gcov (profiling and coverage).

A real gnatmake run from this course: compile, bind (with -Es for symbolic tracebacks), and link.

• Compile (gcc -c → .o + .ali) → bind (gnatbind) → link (gnatlink). • gnatmake automates all three, incrementally. • -bargs / -largs pass switches to the binder / linker.

$ gnatmake -g -gnat2022 crash.adb -bargs -Es
gcc -c -g -gnat2022 crash.adb
gnatbind -Es -x crash.ali
gnatlink crash.ali -g

GPR Project Files and gprbuild

A .gpr file describes a project declaratively: where the sources are, where objects and executables go, which units are mains, and the switches for each tool. Scenario variables (external ("BUILD", "debug")) switch configurations from the command line (-XBUILD=release). Projects can 'with' other projects, build static or shared libraries (Library_Name, Library_Dir, Library_Kind), and mix languages (for Languages use ("Ada", "C")). gprbuild builds them and gprclean cleans them. Current gnatmake no longer accepts project files: in this course's toolchain, 'gnatmake -P hello.gpr' printed 'gnatmake: project files are no longer supported by gnatmake; use gprbuild instead'. That toolchain did not include gprbuild; Alire installs it.

hello.gpr: build with 'gprbuild -P hello.gpr' (debug) or 'gprbuild -P hello.gpr -XBUILD=release'.

Real output from GNAT FSF 16.1 — use gprbuild for .gpr files.

• .gpr: Source_Dirs, Object_Dir, Exec_Dir, Main, tool packages. • Scenario variables: external ("NAME", default) + -XNAME=value. • Library projects, multi-language projects, with-ed projects. • gprbuild / gprclean; gnatmake -P is no longer supported.

project Hello is
   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Exec_Dir use "bin";
   for Main use ("hello.adb");

   type Build_Kind is ("debug", "release");
   Build : Build_Kind := external ("BUILD", "debug");

   package Compiler is
      case Build is
         when "debug" =>
            for Default_Switches ("Ada") use ("-g", "-gnat2022", "-gnata", "-gnatwa");
         when "release" =>
            for Default_Switches ("Ada") use ("-O2", "-gnat2022", "-gnatn");
      end case;
   end Compiler;

   package Binder is
      for Default_Switches ("Ada") use ("-Es");   --  symbolic tracebacks
   end Binder;
end Hello;
$ gnatmake -P hello.gpr
gnatmake: project files are no longer supported by gnatmake; use gprbuild instead

Alire: the Ada Package Manager

Alire (alr) manages toolchains and dependencies. 'alr init --bin my_app' creates a crate with a manifest (alire.toml) and a generated GPR file; 'alr with some_crate' adds a dependency from the community index (JSON, HTTP, AWS web server, GtkAda, SDL, AUnit, and hundreds more); 'alr build' and 'alr run' build with gprbuild using a consistent toolchain; 'alr toolchain --select' chooses GNAT and gprbuild versions; 'alr install gnatprove' installs SPARK tools. Alire was not installed for this course, so the commands below are shown without output.

A typical Alire workflow (commands only).

• alr init / with / build / run / test / publish. • alire.toml manifest; generated .gpr file. • Installs toolchains (GNAT, gprbuild, gnatprove).

gnatmake -P project.gpr gprbuild -P project.gpr Current gnatmake rejects project files (verified).

-- hard-coding debug switches, editing them by hand for releases -- a scenario variable in the.gpr, selected with -XBUILD=release

Scenario variables keep all build configurations in one declarative file.

-- copying library sources into your project alr with library_name

Alire tracks versions and dependencies reproducibly.

Which tool checks whole-program consistency and generates elaboration code? A. gcc B. gnatbind C. gnatlink D. gnatls

Which tool builds .gpr projects today? A. gnatmake -P B. gprbuild C. make D. gnatchop

B. (gnatmake reports that project files are no longer supported.)

How do you select the release scenario in a GPR build? A. --release B. -XBUILD=release C. -O3 D. BUILD=release gnatmake

What does 'alr with aunit' do? A. Runs tests B. Adds the AUnit crate as a dependency C. Compiles AUnit D. Nothing

Write a GPR file for a static library 'mathlib' with sources in src/.

library project Mathlib is for Source_Dirs use ("src"); for Object_Dir use "obj"; for Library_Name use "mathlib"; for Library_Dir use "lib"; for Library_Kind use "static"; end Mathlib;

Other projects use it with 'with "mathlib.gpr";' and gprbuild builds the library before its clients.

alr init --bin my_app      # new executable crate
cd my_app
alr with aunit             # add a dependency
alr build                  # gprbuild with the selected toolchain
alr run                    # build and run
alr toolchain --select     # choose GNAT / gprbuild versions