How to create ada lib.a and link to C [Updated]

I'm trying to build an ada library and have tried several different things. I tried to compile the project with makefiles and try to build a library from all .o files It didn't seem to work as expected. Then I asked adacore support and they directed me to use the .gpr files for the ada and c projects and with ada.gpr which should create the library. It almost worked, but when it tries to compile ada I get undefined references.

What I've tried: Command line:

ar rc libmy_lib.a *.o

      

and when i try to read what's in lib

ld libmy_lib.a

      

I am getting this error ld: warning: cannot find entry symbol _start; do not set start address

Project files: My project file ada prj.gpr

project Prj is
for Source_Dirs use ("source1/", "source2", ....);
for Object_Dir use ".";

for Languages use ("Ada");
for Library_Name use "test";
for Library_Dir use "lib";
for Library_Interface use (
 --All my ada packages
        );

package Naming is
      for Spec_Suffix ("ada") use ".1.ada";
      for Body_Suffix ("ada") use ".2.ada";
      for Separate_Suffix use ".2.ada";
      for Dot_Replacement use ".";
   end Naming;

   package Compiler is
      for Default_Switches ("ada") use ("-v", "-g", "-gnato", "-gnatwa", "-gnatQ", "-gnat05");
   end Compiler;

   package Builder is
      for Global_Compilation_Switches ("Ada") use ("-gnat95");
   end Builder;

   package Ide is
  end Ide;

end Prj;

      

My c project file is c_main.gpr

with "prj.gpr";
project C_Main is
for Source_Dirs use ("source_c_1/", "source_c_2/");
for Languages use ("C");
for Main use ("source_c_1/main.c");
end C_Main;

      

When I run the gprbuild c_main.gpr command

I have two different errors: At the beginning it is undefined links to some packages that are part of my ada code, and it comes that in the gnat.adb files I did not know what exists. This is how the library I'm drawing is broken. The second is that the fields of certain packages cannot be found / do not exist even though the code is compiled and run. This is giving me errors indicating that the fields do not exist in ada code.

TL; DR: I have an ada project in 3 different directories and I want to create a library from them. Then a link to the C test program. Ultimately I just need to provide the library file. The command line would be better. I don't want to deal with project files.

+3


source to share


1 answer


There are big problems with building a static library libtest.a

.

First, the Ada code is most likely called on the Ada Runtime System (RTS). If you are building a static library, you (or your users) will need to call explicitly in Ada RTS, whether you are using it gprbuild

or not. Therefore neither

gcc main_c.c -ltest

      

nor

gprbuild -P c_main

      

will be sufficient; you will get failures like this (and worse):

$ gcc main.c -Lada/lib -ltest
Undefined symbols for architecture x86_64:
  "_ada__calendar__delays__delay_for", referenced from:
      _Hello in libtest.a(hello.o)
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status

      

Second, Ada code may (will!) Require development to be done when the program is run. When it gprbuild

creates a library, it adds functions testinit()

that your C code must call before calling any interface of the library, and testfinal()

to be called after all uses of the library (most people don't bother).

The first problem is creating a dynamic link library ( .dll

on Windows, .so

Linux and other Unix systems, .dylib

on Mac OS X). For this you say for Library_Kind use "dynamic";

. (Note that while the dynamic link library knows what other libraries it needs, it may not know where to find them, so you'll have to put them in the path of finding the loader library).

The twist of the second problem is to create what AdaCore calls the standalone DLL and automatically initialize it.

To do this, you need to add two attributes:

  • for Library_Interface use (...);

    specifies a list of unit names that you want to see outside the library. The effect is to include in the library only files with names and names .ali

    ; if only the callers are to be from C, you probably only need to call it.
  • for Library_Auto_Init use "true";

    - I think this is indeed the default.

I have installed a small example (on Mac OS X, GNAT GPL 2014).

Subdirectory ada

Project file,

library project Prj is
   for Languages use ("ada");
   for Library_Name use "test";
   for Library_Kind use "dynamic";
   for Library_Interface use ("hello");
   for Library_Auto_Init use "true";
   for Library_Src_Dir use "include";
   for Library_Dir use "lib";
   for Source_Dirs use (".");
   for Object_Dir use ".build";
end Prj;

      

hello.ads,

function Hello return Integer;
pragma Export (C, Hello, "Hello");

      

hello.adb,

with Number;
function Hello return Integer is 
begin
   delay 0.001;            -- so the tasking runtime gets called in
   return Number.Value;
end Hello;

      

number.ads,

package Number is
   pragma Elaborate_Body;
   Value : Integer := 0;   -- before elaboration
end Number;

      

and number.adb

package body Number is
begin
   Value := 42;            -- after elaboration
end Number;

      



Parent directory

Project file,

with "ada/prj";
project C_Main is
   for Source_Dirs use (".");
   for Languages use ("c");
   for Main use ("main.c");
   for Exec_Dir use ".";
   for Object_Dir use ".build";
end C_Main;

      

and main.c

#include <stdio.h>

extern int Hello(void);

int main() {
  int hello = Hello();
  printf("Hello returned %d.\n", hello);
  return 0;
}

      

Line

$ gprbuild -p -P c_main
gcc -c main.c
gcc -c -fPIC number.adb
gcc -c -fPIC hello.adb
gprlib test.lexch
gnatbind -n -o b__test.adb -Ltest -a /Users/simon/tmp/crychair/ada/.build/number.ali ...
gcc -c -x ada -gnatA -gnatws b__test.adb -o b__test.o ...
gcc -dynamiclib -shared-libgcc -o /Users/simon/tmp/crychair/ada/lib/libtest.dylib ... /Users/simon/tmp/crychair/ada/.build/number.o ...
ar cr libc_main.a ...
ranlib -c libc_main.a
gcc main.o -o main

      

and doing:

$ ./main
Hello returned 42.

      


To distribute your library to C users on another machine, without the Ada runtime set, you need to package the packages libtest.so

(or .dylib

or .dll

) and the required Ada shared libraries.

On a Unix system, you will find this using ldd libtest.so

Search libgnat*.so

and libgnarl*.so

. You should find them in the compiler's search path (usually the last line in the Object Search Path section of the output gnatls -v

). Typically, there will be symbolic links:

libgnat.so       ->      libgnat.1.so
libgnat.1.so     ->      libgnat.1.0.0.so
libgnat.1.0.0.so         (the real thing)

      

Place the shared libraries and symlinks in a directory with libtest.so

, say product/

then your users should be able to link to

gcc main.c -o main -Lproduct -ltest

      

or maybe

gcc main.c -o main -Lproduct -ltest -lgnat -lgnarl

      

Depending on your OS, the resulting executable may not be able to find the shared libraries at runtime.

One way is to place libraries where the loader already looks like, for example /usr/local/lib

(in this case you won't need it -Lproduct

).

Another way is to tell the bootloader where to look by setting an environment variable ( LD_LIBRARY_PATH

on Linux, DYLD_LIBRARY_PATH

on Mac OS X).

The third way is to tell the linker to store the path in the executable:

gcc main.c -o main -Lproduct -ltest -lgnat -lgnarl -Wl,-rpath,$PWD/product

      

works on Mac OS X will probably be on Linux.

+5


source







All Articles