Custom Incremental Builder
The GPR2 build infrastructure provides a complete framework for implementing
incremental builders on top of the project model. It is used by gprbuild
but is fully available to third-party tools. The key packages are:
GPR2.Build.Tree_Db- persistent build database and action DAG.GPR2.Build.Actions- abstract base type for a single build step.GPR2.Build.Artifacts- abstract base type for the inputs and outputs that connect actions to each other.GPR2.Build.Actions_Population- populates the action graph from a project tree using the standard GPR2 build actions.GPR2.Build.Actions_Scheduler- parallel action execution engine.
Overview
A build proceeds in four stages:
Load the project tree (
Tree.Load).Populate sources (
Tree.Update_Sources).Populate the action graph - either via
Actions_Population.Populate_Actionsfor standard GPR builds, or by inserting custom actions directly intoTree.Artifacts_Database.Execute the graph (
Tree.Artifacts_Database.Execute).
The build database
Tree.Artifacts_Database returns an access to the
GPR2.Build.Tree_Db.Object for the tree. The database is created
automatically when the tree is loaded.
Db : constant GPR2.Build.Tree_Db.Object_Access :=
Tree.Artifacts_Database;
The database holds a directed acyclic graph of actions connected by artifacts. Each action’s output artifacts become input artifacts of downstream actions, establishing the dependency order for execution. Signature checksums persist on disk so that unchanged actions are skipped on the next run.
Populating the standard action graph
For standard GPR builds (compile, bind, link), use
GPR2.Build.Actions_Population.Populate_Actions:
with GPR2.Build.Actions_Population;
with GPR2.Build.Options;
Build_Opts : GPR2.Build.Options.Build_Options;
-- Build_Opts.Mains may be set to restrict to specific mains;
-- leave empty to build all mains from the root project.
if not GPR2.Build.Actions_Population.Populate_Actions
(Tree => Tree,
Options => Build_Opts)
then
return;
end if;
if not Db.Propagate_Actions then
return;
end if;
Populate_Actions inserts compile, bind, and link actions for every
source in the tree. Propagate_Actions then calls
On_Tree_Propagation on each action to resolve cross-action
dependencies (e.g. the Ada binder closure).
Executing the graph
Pass a scheduler and options to Db.Execute:
with GPR2.Build.Actions_Scheduler;
Scheduler : GPR2.Build.Actions_Scheduler.Object;
Sched_Opts : GPR2.Build.Actions_Scheduler.Options;
Sched_Opts.Jobs := 0; -- 0 = auto-detect CPU count
Sched_Opts.Stop_On_Fail := True;
case Db.Execute (Scheduler, Sched_Opts) is
when GPR2.Build.Actions_Scheduler.Success => null;
when GPR2.Build.Actions_Scheduler.Errors =>
-- some actions reported errors
return;
when GPR2.Build.Actions_Scheduler.Failed =>
-- some actions failed to launch
return;
end case;
Key Options fields:
JobsParallel job count;
0auto-detects the number of CPUs.ForceRe-execute all actions regardless of signature validity.
Stop_On_FailAbort on first failure (default
True).Keep_Temp_FilesPreserve temporary files after execution (useful for debugging).
Script_FileIf defined, records all executed commands to this file.
Show_ProgressEmit progress counters as actions are dispatched.
No_Warnings_ReplayWhen set, warnings from skipped (up-to-date) actions are suppressed rather than replayed.
Force_JobserverWhen set, abort if no Make jobserver protocol is available.
Actions
GPR2.Build.Actions.Object is the abstract base type for a build step.
Each action owns a view (its context for looking up attributes and
directories) and a signature (checksums of all its inputs and outputs).
Built-in concrete actions provided by the library:
GPR2.Build.Actions.CompileCompiles one source file for any language.
GPR2.Build.Actions.Compile.AdaAda-specific compilation (extends Compile).
GPR2.Build.Actions.Ada_BindRuns the Ada binder (
gnatbind) for one main.GPR2.Build.Actions.Post_BindCompiles the binder-generated body.
GPR2.Build.Actions.LinkLinks an executable or shared library.
GPR2.Build.Actions.Link.PartialPartial link step used for standalone libraries.
Action lifecycle hooks
Each action participates in the build graph via the following hooks, called in this order:
On_Tree_InsertionCalled when the action is added to the database. The action declares its output artifacts and may insert follow-up actions (e.g. a bind action inserts the post-bind compile action here).
On_Tree_PropagationCalled after initial population. Used to expand dependencies dynamically (e.g. the binder walks the Ada closure to pull in all required compilation units). Default implementation does nothing.
Compute_CommandBuilds the command line just before execution. Also called when the signature is valid (to include the command line in the signature check) with
Signature_Only => True.Pre_CommandCalled immediately before the process is spawned. Not called when the action is skipped. Default returns
True.Post_CommandCalled after the process completes, is skipped, or fails. Default returns
True.On_Static_CompletionReplaces
Pre_Command/Post_Commandwhen actions are populated but not executed (e.g.gprinstall). Must not modify artifacts. Default returnsTrue.
Artifacts
GPR2.Build.Artifacts.Object is the interface that connects actions.
An action’s outputs become inputs to downstream actions, establishing the
DAG edges. Concrete artifact types:
Artifacts.Files.ObjectA filesystem file (source, object, library, …).
Artifacts.Object_File.ObjectA compiled object file.
Artifacts.Library.ObjectA static or shared library.
Artifacts.Key_Value.ObjectAn abstract key/value pair; used for ordering actions that do not produce a file (e.g. the UID artifact that establishes execution order without file dependencies).
Artifacts.Source_Files.ObjectA source file as a build artifact.
Wiring actions to artifacts is done via the database:
-- Register an output artifact for an action
if not Db.Add_Output (Action.UID, My_Object_File) then
-- artifact already owned by another action
end if;
-- Register an input dependency
Db.Add_Input
(Action => Downstream_Action.UID,
Artifact => My_Object_File,
Explicit => True);
Implementing a custom action
Extend GPR2.Build.Actions.Object, implement Action_Id, and
override the mandatory primitives:
with GPR2.Build.Actions;
with GPR2.Build.Tree_Db;
with GPR2.Build.Command_Line;
type My_Action_Id is new GPR2.Build.Actions.Action_Id with record
View : GPR2.Project.View.Object;
Input : GPR2.Path_Name.Object;
end record;
overriding function View
(Self : My_Action_Id) return GPR2.Project.View.Object
is (Self.View);
overriding function Action_Class
(Self : My_Action_Id) return Value_Type
is (+"MyAction");
overriding function Language
(Self : My_Action_Id) return Language_Id
is (No_Language);
overriding function Action_Parameter
(Self : My_Action_Id) return Value_Type
is (Value_Type (Self.Input.Simple_Name));
type My_Action is new GPR2.Build.Actions.Object with record
Id : My_Action_Id;
Input : GPR2.Path_Name.Object;
end record;
overriding function UID
(Self : My_Action) return GPR2.Build.Actions.Action_Id'Class
is (Self.Id);
overriding function Working_Directory
(Self : My_Action) return GPR2.Path_Name.Object
is (Self.View.Object_Directory);
overriding function On_Tree_Insertion
(Self : My_Action;
Db : in out GPR2.Build.Tree_Db.Object) return Boolean
is
begin
-- Register output artifacts here
return True;
end On_Tree_Insertion;
overriding procedure Compute_Command
(Self : in out My_Action;
Slot : Positive;
Cmd_Line : in out GPR2.Build.Command_Line.Object;
Signature_Only : Boolean)
is
begin
Cmd_Line.Add_Argument ("my-tool");
Cmd_Line.Add_Argument (String (Self.Input.Value));
end Compute_Command;
overriding procedure Compute_Signature
(Self : in out My_Action;
Check_Checksums : Boolean)
is
begin
-- Register inputs/outputs in Self.Signature for change detection
null;
end Compute_Signature;
Once implemented, insert the action into the database before calling
Execute:
Action : My_Action := ...;
if not Db.Add_Action (Action) then
-- action already present
end if;