Ada API tutorial

Preliminary setup

In order to compile/run GPR2 tutorial examples you need to have GNAT compiler and libgpr2 library installed. Preliminary setup can be checked running

# Checking the GPR2 library installation
$ gprls -P gpr2

The first thing to do in order to use gpr2 is to load a project file:

with Ada.Text_IO; use Ada.Text_IO;

with GPR2.Containers;
with GPR2.Context;
with GPR2.Path_Name;
with GPR2.Project.Attribute;
with GPR2.Project.Attribute_Index;
with GPR2.Project.Registry.Attribute;
with GPR2.Project.Registry.Pack;
with GPR2.Project.Source;
with GPR2.Project.Tree;
with GPR2.Project.Typ;
with GPR2.Project.Unit_Info;
with GPR2.Project.Variable;
with GPR2.Project.View;
with GPR2.Source_Info;
with GPR2.Unit;

procedure Main is
   Context : GPR2.Context.Object;
   --  Context used to set scenarios

   Tree    : GPR2.Project.Tree.Object;
   --  "gpr2_tutorial.gpr" tree.

--  Insert tutorials here.

begin
   Tree.Load_Autoconf
     (Filename => GPR2.Path_Name.Create_File ("gpr2_test.gpr"),
      Context  => Context);
   Tree.Log_Messages.all.Output_Messages;

--  Uncomment tutorial call
--   Views;
--   Scenarios;
--   Packages;
--   Attributes;
--   Types;
--   Variables;
--   Sources;
--   Units;
--   Registry;
end Main;

This very simple program will allow us to make sure the build environment is properly set to use gpr2. Save the above in a main.adb source file, and then write the following project file to gpr2_test.gpr:

with "gpr2";

project GPR2_Test is
   for Main use ("main.adb");
   for Object_Dir use "obj";
end GPR2_Test;

Now you can run gprbuild to compile the test program:

$ gprbuild -Pgpr2_test

This command should return without error and create an executable in obj/main or obj\main.exe depending on your platform. The last step is to check if the program works properly: it should do nothing, so no errors expected!

# Empty program output
$ obj/main
$

Views

When loading a gpr file in a Tree object a lot of gpr & cgpr files are loaded. In autoconf mode a configuration project is automatically generated. A configuration, a runtime project and all imported, extended or aggregated projects are recursively loaded.

All of these projects are available through GPR2.Project.View.Object objects. After a successful load, Tree.Root_Project contains the project passed when loading the tree.

procedure Views is
begin
   --  How to get views of a loaded tree.

   New_Line;
   Put_Line ("GPR2 project views");

   --  root, configuration & runtime projects

   Put_Line ("gpr2_test.gpr at " & Tree.Root_Project.Path_Name.Value);
   Put_Line ("generated configuration project at " &
               Tree.Configuration.Corresponding_View.Path_Name.Value);
   Put_Line ("used runtime project at " &
               Tree.Runtime_Project.Path_Name.Value);

   --  View can be retrieved by name

   Put_Line ("gpr2.gpr at " &
               Tree.Root_Project.View_For ("gpr2").Path_Name.Value);

   --  Using a configurable iterator

   New_Line;
   for C in Tree.Iterate loop
      Put_Line ("view: " &
                  GPR2.Project.Tree.Element (C).Path_Name.Value);
   end loop;

   --  Using view's accessor functions

   New_Line;
   for V of Tree.Root_Project.Imports loop
      Put_Line ("imported: " & V.Path_Name.Value);
   end loop;

end Views;

As for all tutorial examples don’t forget to add procedure call in Main procedure.

begin
   Tree.Load_Autoconf
     (Filename => GPR2.Path_Name.Create_File ("gpr2_test.gpr"),
      Context  => Context);
   Tree.Log_Messages.all.Output_Messages;

--  Uncomment tutorial call
   Views;
--   Scenarios;
--   Packages;
--   Attributes;
--   Types;
--   Variables;
--   Sources;
--   Units;
--   Registry;
end Main;

Scenarios

Various project properties can be modified based on scenarios. The above code shows how scenarios are handled by the GPR2 library.

procedure Scenarios is
   GPR2_View : constant GPR2.Project.View.Object :=
                 Tree.Root_Project.View_For ("gpr2");
begin
   --  Print current GPR2 Library_Kind using default scenarios values.

   New_Line;
   Put_Line ("Library_Kind (default): " &
               String (GPR2_View.Library_Kind));

   --  Change LIBRARY_TYPE to relocatable & print modified Library_Kind.

   Context.Insert ("LIBRARY_TYPE", "relocatable");
   Tree.Set_Context (Context);
   Put_Line ("Library_Kind (relocatable): " &
               String (GPR2_View.Library_Kind));
end Scenarios;

Packages

This tutorial shows how view’s packages are listed. Attributes, variables and types parts will explain how packages are handled when accessing package content.

procedure Packages is
   GPR2_View : constant GPR2.Project.View.Object :=
                 Tree.Root_Project.View_For ("gpr2");

    procedure Print (With_Defaults : Boolean; With_Config : Boolean) is
      Print_Comma : Boolean := False;
   begin
      if With_Defaults then
         Put ("including packages defined by default values: ");
      end if;
      if With_Config then
         Put ("including packages defined by configuration file: ");
      end if;
      for Id of GPR2_View.Packages (With_Defaults, With_Config) loop
         if Print_Comma then
            Put (", ");
         end if;
         Print_Comma := True;
         Put (GPR2.Image (Id));
      end loop;
      New_Line;
   end Print;
begin
   New_Line;
   Put_Line ("GPR2 project packages");
   Print (False, False);
   Print (True, False);
   Print (False, True);
end Packages;

Attributes

This tutorial shows how attributes can be accessed or listed.

procedure Attributes is
   GPR2_View : constant GPR2.Project.View.Object :=
                 Tree.Root_Project.View_For ("gpr2");

   procedure Print (Attribute : GPR2.Project.Attribute.Object);
   procedure Print (Attribute : GPR2.Project.Attribute.Object) is
      use GPR2;
      use GPR2.Project.Registry.Attribute;
   begin
      Put ("for " & Image (Attribute.Name.Id.Attr));
      if Attribute.Has_Index then
         Put (" (""" & Attribute.Index.Text & """");
         if Attribute.Index.Has_At_Pos then
            Put (" at " & Attribute.Index.At_Pos'Image);
         end if;
         Put (")");
      end if;
      if Attribute.Kind = GPR2.Project.Registry.Attribute.Single then
         Put (" use """ & Attribute.Value.Text & """");
         if Attribute.Value.Has_At_Pos then
            Put (" at " & Attribute.Value.At_Pos'Image);
         end if;
      else
         Put (" use " & GPR2.Containers.Image (Attribute.Values));
      end if;
      Put (";");
      if Attribute.Is_Default then
         Put (" -- default value");
      end if;
      if Attribute.Is_From_Config then
         Put (" -- from configuration project");
      end if;
      New_Line;
   end Print;

begin
   New_Line;
   Put_Line ("Compiler.Default_Switches (""Ada"") attribute");
   Print
     (GPR2_View.Attribute
        (Name  => GPR2.Project.Registry.Attribute.Compiler.Default_Switches,
         Index => GPR2.Project.Attribute_Index.Create (GPR2.Ada_Language)));

   New_Line;
   Put_Line ("GPR2 project attributes");
   for Attribute of GPR2_View.Attributes loop
      Print (Attribute);
   end loop;
end Attributes;

Types

This tutorial shows how variables types can be accessed/listed.

procedure Types is
   GPR2_View : constant GPR2.Project.View.Object :=
                 Tree.Root_Project.View_For ("gpr2");

   procedure Print (Typ : GPR2.Project.Typ.Object) is
      Print_Comma : Boolean := False;
   begin
      Put (String (Typ.Name.Text) & ", values: ");
      for Value of Typ.Values loop
         if Print_Comma then
            Put (", ");
         else
            Print_Comma := True;
         end if;
         Put (Value.Text);
      end loop;
      New_Line;
   end Print;
begin
   New_Line;
   Put_Line ("GPR2 project types");
   Print (GPR2_View.Typ ("bool"));

   New_Line;
   Put_Line ("GPR2 project types");
   if GPR2_View.Has_Types then
      for Typ of GPR2_View.Types loop
         Print (Typ);
      end loop;
   end if;
end Types;

Variables

This tutorial shows how project-level and package-level variables can be listed and accessed.

procedure Variables is
   GPR2_View : constant GPR2.Project.View.Object :=
                 Tree.Root_Project.View_For ("gpr2");

   procedure Print (Variable : GPR2.Project.Variable.Object);
   procedure Print (Variable : GPR2.Project.Variable.Object) is
      use GPR2.Project.Registry.Attribute; --  "=" function visibility
   begin
      Put (String (Variable.Name.Text));
      if Variable.Has_Type then
         Put (", type: " & String (Variable.Typ.Name.Text));
      else
         Put (" with no type");
      end if;
      if Variable.Kind = GPR2.Project.Registry.Attribute.Single then
         Put (", value: " & String (Variable.Value.Text));
      else
         Put (", values: " & GPR2.Containers.Image (Variable.Values));
      end if;
      New_Line;
   end Print;
begin
   New_Line;
   Put_Line
     ("GPR2 project build & compiler.langkit_parser_options variable");
   Print (GPR2_View.Variable ("build"));
   if GPR2_View.Has_Variables
     (GPR2.Project.Registry.Pack.Compiler, "langkit_parser_options")
   then
      Print (GPR2_View.Variable
             (GPR2.Project.Registry.Pack.Compiler,
                "langkit_parser_options"));
   end if;

   New_Line;
   Put_Line ("GPR2 project variables");
   if GPR2_View.Has_Variables then
      for Variable of GPR2_View.Variables loop
         Print (Variable);
      end loop;
   end if;

   New_Line;
   Put_Line ("GPR2 project Compiler package variables");
   if GPR2_View.Has_Variables (GPR2.Project.Registry.Pack.Compiler) then
      for Variable of
        GPR2_View.Variables (GPR2.Project.Registry.Pack.Compiler) loop
         Print (Variable);
      end loop;
   end if;
end Variables;

Sources

This tutorial shows how projects sources are parsed, listed, and accessed.

procedure Sources is
   GPR2_View : constant GPR2.Project.View.Object :=
                 Tree.Root_Project.View_For ("gpr2");
   Source    : GPR2.Project.Source.Object;
   Part      : GPR2.Project.Source.Source_Part;
   use GPR2.Unit;
begin

   --  Update_sources required after load to to get a source unless
   --  Tree.For_Each_Source or View.Sources was called.

   Source := GPR2_View.Source ("gpr2-project-tree.ads");
   if not Source.Is_Defined then
      Tree.Update_Sources
        (Stop_On_Error => True,
         With_Runtime  => False,
         Backends      => GPR2.Source_Info.All_Backends);

      Source := GPR2_View.Source ("gpr2-project-tree.ads");
   end if;

   New_Line;
   Put_Line ("gpr2-project-tree.ads source");
   Source := GPR2_View.Source ("gpr2-project-tree.ads");
   Put_Line (" kind is " & Source.Kind'Image);
   Put_Line (" unit name is " & String (Source.Unit_Name));

   if Source.Has_Other_Part then
      Put_Line
        (" other part is"
         & String (Source.Other_Part.Source.Path_Name.Simple_Name));
   end if;

   --  Separate file.

   Source := GPR2_View.Source ("gpr2-project-tree-load_autoconf.adb");
   if Source.Is_Defined and then Source.Kind = GPR2.Unit.S_Separate then
      Put_Line ("gpr2-project-tree-load_autoconf.adb unit name is "
                & String (Source.Unit_Name));
      Part := Source.Separate_From (GPR2.No_Index);
      Put_Line ("gpr2-project-tree-load_autoconf.adb separate from "
                & String (Part.Source.Path_Name.Simple_Name));
   end if;
end Sources;

Units

This tutorial shows how units are listed and accessed.

procedure Units is
   GPR2_View : constant GPR2.Project.View.Object :=
                 Tree.Root_Project.View_For ("gpr2");
   Unit : GPR2.Project.Unit_Info.Object :=
            GPR2_View.Unit ("gpr2.project.tree");

   procedure Print
     (Prefix : String; SUI : GPR2.Unit.Source_Unit_Identifier) is
      use GPR2;
   begin
      Put (Prefix & String (SUI.Source.Simple_Name));
      if SUI.Index /= No_Index then
         Put (" at " & SUI.Index'Image);
      end if;
      New_Line;
   end Print;
begin
   if GPR2_View.Units.Is_Empty then

      --  Update_sources required after load to get a source or unit
      --  unless Tree.For_Each_Source or View.Sources was called.
      Put_Line ("Tree.Update_Sources");

      Tree.Update_Sources
        (Stop_On_Error => True,
         With_Runtime  => False,
         Backends      => GPR2.Source_Info.All_Backends);

   end if;

   Unit := GPR2_View.Unit ("gpr2.project.tree");

   New_Line;
   Put_Line ("Unit:" & String (Unit.Name));
   if Unit.Has_Spec then
      Print (" Specification in ", Unit.Spec);
   end if;
   if Unit.Has_Body then
      Print (" Body in ", Unit.Main_Body);
   end if;
   declare
      Separates : constant GPR2.Unit.Source_Unit_Vectors.Vector :=
                    Unit.Separates;
   begin
      if not Separates.Is_Empty then
         for S of Separates loop
            Print (" Separate in ", S);
         end loop;
      end if;
   end;
 end Units;

Custom packages and attributes

This tutorial shows how custom packages and attributes can be added to gpr2 package/attribute registry. This should be done before loading projects.

procedure Registry is
   use GPR2;

   Custom_Package_Id   : constant Package_Id := +"custom";
   Custom_Attribute_Id : constant Q_Attribute_Id :=
                           (Custom_Package_Id, +"new_attribute");
begin
   --  Add package

   Project.Registry.Pack.Add
     (Name     => Custom_Package_Id,
      Projects => GPR2.Project.Registry.Pack.Everywhere);

   --  Add new attribute
   GPR2.Project.Registry.Attribute.Add
     (Name                 => Custom_Attribute_Id,
      Index_Type           =>
        Project.Registry.Attribute.FileGlob_Or_Language_Index,
      Index_Optional       => True,
      Value                => Project.Registry.Attribute.List,
      Value_Case_Sensitive => True,
      Is_Allowed_In        => Project.Registry.Attribute.Everywhere,
      Config_Concatenable  => True);
end Registry;