9.2. Dealing with aggregate projects
Processing aggregate projects with Libadalang is peculiar. Please get familiar with the Aggregate projects section before reading this.
9.2.1. General examples
9.2.1.1. Input sources
Get the projects files and sources from the Aggregate projects section.
9.2.1.2. Sample code
9.2.1.2.1. Ada
with GNATCOLL.Projects;
with GNATCOLL.VFS;
with Libadalang.Analysis;
with Libadalang.Project_Provider;
-- Create a unit provider for the "arch32/arch.gpr" project, loaded through
-- the "agg.gpr" project.
declare
package Prj renames GNATCOLL.Projects;
package VFS renames GNATCOLL.VFS;
package LAL renames Libadalang.Analysis;
PT : Prj.Project_Tree_Access;
Env : Prj.Project_Environment_Access;
P : Prj.Project_Type;
UP : LAL.Unit_Provider_Reference;
Ctx : LAL.Analysis_Context;
begin
-- Load the "agg.gpr" project
PT := new Prj.Project_Tree;
Prj.Initialize (Env);
PT.Load (Root_Project_Path => VFS.Create ("agg.gpr"),
Env => Env);
-- Fetch the "arch/arch32.gpr" project
P := PT.Project_From_Path (VFS.Create ("arch/arch32.gpr"));
-- Create the unit provider, then the analysis context
UP := Libadalang.Project_Provider.Create_Project_Unit_Provider
(PT, P, Env);
Ctx := LAL.Create_Context (Unit_Provider => UP);
end;
-- Create several project providers so that one provider has at most one
-- source file per unit name/unit kind.
declare
package Prj renames GNATCOLL.Projects;
package VFS renames GNATCOLL.VFS;
package LAL renames Libadalang.Analysis;
PT : Prj.Project_Tree_Access;
Env : Prj.Project_Environment_Access;
PAPs : Libadalang.Project_Provider.Provider_And_Projects_Array_Access;
begin
-- Load the "agg.gpr" project
PT := new Prj.Project_Tree;
Prj.Initialize (Env);
PT.Load (Root_Project_Path => VFS.Create ("agg.gpr"),
Env => Env);
-- Create one analysis context per unit provider
PAPs := Libadalang.Project_Provider.Create_Project_Unit_Providers (PT);
for PAP of PAPs.all loop
declare
Ctx : constant LAL.Analysis_Context :=
LAL.Create_Context (Unit_Provider => PAP.Provider);
begin
-- Do your processings...
null;
end;
end loop;
Libadalang.Project_Provider.Free (PAPs);
-- Free allocated resources
Prj.Free (PT);
Prj.Free (Env);
end;
9.2.1.2.2. Python
# Create a unit provider for the "arch32/arch.gpr" project, loaded through
# the "agg.gpr" project. Then create an analysis context using it.
up = libadalang.UnitProvider.for_project(
'agg.gpr', project='arch32/arch.gpr'
)
ctx = libadalang.AnalysisContext(unit_provider=up)
9.2.2. Creating one provide/context for each aggregated project
This complete example demonstrates how to use the GNATCOLL.Projects
API in
order to analyze each aggregated project separately with Libadalang.
Note that there is no equivalent in Python as the GNATCOLL project does not provide a Python API to analyze projects files.
9.2.2.1. Input sources
-- arch32/arch.gpr
project Arch is
for Object_Dir use "obj";
end Arch;
-- arch32/arch.ads
package Arch is
type Target_Address is mod 2 ** 32;
end Arch;
-- arch64/arch.gpr
package Arch is
type Target_Address is mod 2 ** 64;
end Arch;
-- arch64/arch.ads
project Arch is
for Object_Dir use "obj";
end Arch;
-- main/main32.gpr
with "../arch32/arch";
project Main32 is
for Object_Dir use "obj-32";
for Main use ("main");
end Main32;
-- main/main64.gpr
with "../arch64/arch";
project Main64 is
for Object_Dir use "obj-64";
for Main use ("main");
end Main64;
-- main/main.adb
with Ada.Text_IO, Arch;
procedure Main is
Last : constant Arch.Target_Address := Arch.Target_Address'Last;
begin
Ada.Text_IO.Put_Line ("Arch.Target_Address'Last =" & Last'Image);
end;
-- agg32.gpr
aggregate project Agg32 is
for Project_Files use ("main/main32.gpr");
end Agg32;
-- agg64.gpr
aggregate project Agg64 is
for Project_Files use ("main/main64.gpr");
end Agg64;
-- agg.gpr
aggregate project Agg is
for Project_Files use ("agg32.gpr", "agg64.gpr");
end Agg;
9.2.2.2. Sample code
with Ada.Containers.Generic_Array_Sort;
with Ada.Text_IO; use Ada.Text_IO;
with GNATCOLL.Projects; use GNATCOLL.Projects;
with GNATCOLL.VFS; use GNATCOLL.VFS;
with Libadalang.Analysis; use Libadalang.Analysis;
with Libadalang.Common; use Libadalang.Common;
with Libadalang.Project_Provider; use Libadalang.Project_Provider;
procedure Run is
-- Sort lists of projects and source files so that execution is
-- deterministic.
function "<" (Left, Right : Project_Type) return Boolean is
(Left.Project_Path < Right.Project_Path);
procedure Sort is new Ada.Containers.Generic_Array_Sort
(Index_Type => Positive,
Element_Type => Virtual_File,
Array_Type => File_Array);
procedure Sort is new Ada.Containers.Generic_Array_Sort
(Index_Type => Positive,
Element_Type => Project_Type,
Array_Type => Project_Array);
procedure Iterate_Aggregated
(Project : Project_Type;
Callback : access procedure (Project : Project_Type));
-- If Project is not an aggregate project, just call Callback on it.
-- Otherwise, recurse on Project's aggregated projects.
procedure Process_Aggregated (Project : Project_Type);
-- Analyze all sources in Project
function Process_Node (Node : Ada_Node'Class) return Visit_Status;
-- If Node is an object declaration, show the corresponding type
-- declaration node.
Env : Project_Environment_Access;
Tree : Project_Tree_Access := new Project_Tree;
------------------------
-- Iterate_Aggregated --
------------------------
procedure Iterate_Aggregated
(Project : Project_Type;
Callback : access procedure (Project : Project_Type))
is
begin
if Project.Is_Aggregate_Project then
declare
Aggregated : Project_Array_Access := Project.Aggregated_Projects;
begin
Sort (Aggregated.all);
for A of Aggregated.all loop
Iterate_Aggregated (A, Callback);
end loop;
Unchecked_Free (Aggregated);
end;
else
Callback (Project);
end if;
end Iterate_Aggregated;
------------------------
-- Process_Aggregated --
------------------------
procedure Process_Aggregated (Project : Project_Type) is
-- Create an analysis context that has a view on all sources in Project
UP : constant Unit_Provider_Reference := Create_Project_Unit_Provider
(Tree, Project, Env,
-- We handle the lifetime of Tree and Env manually (see the Free
-- calls at the end of this source file), so consider that UP won't
-- own them.
Is_Project_Owner => False);
Context : constant Analysis_Context :=
Create_Context (Unit_Provider => UP);
Sources : File_Array_Access := Project.Source_Files (Recursive => True);
begin
Put_Line ("== Processing project: " & Project.Name & " ==");
Sort (Sources.all);
for Filename of Sources.all loop
declare
Unit : constant Analysis_Unit :=
Context.Get_From_File (+Filename.Full_Name);
begin
Put_Line ("In " & (+Filename.Base_Name) & ":");
Unit.Root.Traverse (Process_Node'Access);
end;
end loop;
Unchecked_Free (Sources);
New_Line;
end Process_Aggregated;
------------------
-- Process_Node --
------------------
function Process_Node (Node : Ada_Node'Class) return Visit_Status is
begin
if Node.Kind = Ada_Object_Decl then
declare
Type_Decl : constant Base_Type_Decl :=
Node.As_Object_Decl.F_Type_Expr.P_Designated_Type_Decl;
begin
Put_Line (" Type for " & Node.Short_Image
& ": " & Type_Decl.Debug_Text);
end;
end if;
return Into;
end Process_Node;
begin
-- Load the input project
Initialize (Env);
Tree.Load (Create (+"agg.gpr"), Env);
-- Process every aggregated project independently
Iterate_Aggregated (Tree.Root_Project, Process_Aggregated'Access);
-- Do not leak project resources
Free (Tree);
Free (Env);
end Run;
9.2.2.3. Expected output
== Processing project: Main32 ==
In arch.ads:
In main.adb:
Type for <ObjectDecl ["Last"] main.adb:3:4-3:68>: type Target_Address is mod 2 ** 32;
== Processing project: Main64 ==
In arch.ads:
In main.adb:
Type for <ObjectDecl ["Last"] main.adb:3:4-3:68>: type Target_Address is mod 2 ** 64;