crimsonking
crimsonking

Reputation: 83

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.

Upvotes: 5

Views: 2446

Answers (2)

Simon Wright
Simon Wright

Reputation: 25501

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 libck.a 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 ckfinal(void);
void foo(void);
void bar(void);

int main()
{
  ckinit();
  printf("calling foo:\n");
  foo();
  printf("calling bar:\n");
  bar();
  ckfinal();
  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

Upvotes: 12

crimsonking
crimsonking

Reputation: 83

Thank you for you great help! Actually, it worked with the following Makefile:

ada_libs := -lgnat -lgnarl

cpp_src := ...
ada_src := ...

library.so : $(cpp_src:.cc=.o) adalib.a
    g++ -o $@ $^ $(ada_libs)

$(cpp_src:.cc=.o) : %.o : %.cc
    g++ -c -o $@ $<

$(cpp_src:.cc=.d) : %.d : %.cc
    g++ -MM -MF $@ $^

$(addprefix objects/,$(ada_src:.adb=.o)) : objects/%.o : %.adb
    gnatmake -c -D objects $^

adabind.adb : $(addprefix objects/,$(ada_src:.adb=.o))
    gnatbind -n -o $@ $(^:.o=.ali)

adabind.ali : adabind.adb
    gnatmake -c -D objects $^

adalib.a : adabind.ali
    ar cur $@ $(^:.ali=.o) objects/*.o

include $(cpp_src:.cc=.d)

Besides this, I had to declare my function as extern "C" in my C++ file.

Thank you a lot, I was almost there but missed to include the ada runtime libraries (-lgnat -lgnarl) while linking.

Upvotes: 2

Related Questions