crimsonking crimsonking - 3 days ago 6
C++ Question

How to build a static library from ADA source that's callable from C++ code?

I need to build a static library with a bunch of code written in ADA that can be called from code written in C/C++.
I've searched through internet and got some knowledge about gnatmake, gnatbind and gnatlink, but still can't get the job done correctly.
Also, I've read there are tools that relies upon some kind of project file.
I'm not interested in those, I just need a bunch of commands to write in a Makefile.
Thanks in advance!

Answer

This answer assumes you’re using the GCC toolchain.

The big hurdle is that Ada code needs elaboration (roughly, the equivalent of calling file-level constructors in C++). gnatbind is the tool that does this, and you use the flag -L:

-Lxyz     Library build: adainit/final renamed to xyzinit/final, implies -n
[...]
-n        No Ada main program (foreign main routine)

As an example, consider Ada source foo.ads,

package Foo is
   procedure Impl
   with
     Convention => C,
     Export,
     External_Name => "foo";
end Foo;

or, if using Ada prior to Ada2012,

package Foo is
   procedure Impl;
   pragma Export (Convention => C, Entity => Impl, External_Name => "foo");
end Foo;

and foo.adb,

with Ada.Text_IO;
package body Foo is
   procedure Impl is
   begin
      Ada.Text_IO.Put_Line ("I am foo");
   end Impl;
begin
   Ada.Text_IO.Put_Line ("foo is elaborated");
end Foo;

and a similar pair of files bar.ads, bar.adb (s/foo/bar/g throughout).

Compile these:

gnatmake foo bar

Bind:

gnatbind -Lck -o ck.adb foo.ali bar.ali

(this will actually generate ck.ads as well as the named ck.adb; these are the code that does the elaboration).

Compile the elaboration code:

gnatmake ck.adb

Generate the library:

ar cr ck.o foo.o bar.o

and you’re nearly ready to roll.

The C main program might look like

#include <stdio.h>

void ckinit(void);
void foo(void);
void bar(void);

int main()
{
  ckinit();
  printf("calling foo:\n");
  foo();
  printf("calling bar:\n");
  bar();
  return 0;
}

(your main is in C++, so you’ll need extern "C" {..., of course).

You’d think that

gcc main.c libck.a

would do the trick. However, libck calls in the Ada runtime. Here (macOS), that means I say

gcc main.c libck.a /opt/gnat-gpl-2016/lib/gcc/x86_64-apple-darwin14.5.0/4.9.4/adalib/libgnat.a

(you can find that path using gcc --print-libgcc-file-name)

The resulting executable runs:

$ ./a.out
bar is elaborated
foo is elaborated
calling foo:
I am foo
calling bar:
I am bar
Comments