****************
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

.. code-block:: shell

   # Checking the GPR2 library installation
   $ gprls -P gpr2


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

.. code-block:: ada

   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``:

.. code-block:: ada

   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:

.. code-block:: shell

    $ 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!

.. code-block:: shell

   # 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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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.

.. code-block:: ada

   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;
