Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 529
crimsonking Avatar asked Nov 29 '16 16:11

crimsonking


People also ask

Which command is used to create static libraries?

ar is used to create static libraries. These are used in software development. And ar is also be used to create package files such as the “.


2 Answers

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
like image 81
Simon Wright Avatar answered Sep 30 '22 05:09

Simon Wright


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.

like image 30
crimsonking Avatar answered Sep 30 '22 05:09

crimsonking