Reputation: 83
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
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
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