With GNAT, there are two jobs which the binder performs: first, checking that all the necessary compilations have been done, so that the program’s closure is consistent, and secondly arranging for elaboration to happen (these jobs are needed for any Ada build system, but they may be implemented differently).
When using gnatmake
, the first of these jobs is usually superfluous, because gnatmake
has already organised all the necessary compilations. It is possible to get this wrong (by, for example, moving a unit to a different library and not deleting its compilation products from the original place) but quite hard!
Elaboration is a feature of Ada that isn’t present in many other languages. There’s explanation at gcc.gnu.org and other places, but for a simple example,
with Foo;
package Bar is
Int : Integer := Foo.Value;
[...]
end Bar;
package Foo is
function Value return Integer;
[...]
end Foo;
we don’t know what Foo.Value
is going to return at compile time, and we may not know until run time (what if it reads a value from the command line?), so Foo.Value
must be in a fit state to be called before Bar
’s initialisation happens.
Bar
’s initialisation happens when Bar
is elaborated, and likewise for Foo
, so it’s gnatbind
’s job to recognise this and arrange that Foo
is elaborated before Bar
.
It does this by emitting calls to packages’ elaboration code in a function (usually called adanit
), and a main()
, which is to be called by the operating system and calls adainit
and then the Ada main program, say program.adb
.
gnatmake
then calls gnatlink
, which takes the gnatbind
-generated code, in Ada in files called b-program.ad[sb]
or b__program.ad[sb]
or b~program.ad[sb]
depending on the vintage of the compiler, compiles it, and links it with the program’s closure to produce the final executable.