How to create an ada lib.a and link to C

后端 未结 1 1956
小鲜肉
小鲜肉 2021-01-02 14:30

I am trying to create an ada library and have tried a few different things. I have tried compiling the project using makefiles and trying to create a library from all the .o

相关标签:
1条回答
  • 2021-01-02 15:04

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

    First, the Ada code is extremely likely to call in the Ada runtime system (RTS). If you create a static library, you (or your users) will need to call in the Ada RTS explicitly, whether or not you use gprbuild. So neither

    gcc main_c.c -ltest
    

    nor

    gprbuild -P c_main
    

    will be enough; you'll 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
    

    Secondly, Ada code might (will!) require elaboration, done at program start. When gprbuild creates the library it adds functions testinit(), which 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).

    A way round the first problem is to create a dynamic library (.dll on Windows, .so on Linux and other Unix systems, .dylib on Mac OS X). To do this, you say for Library_Kind use "dynamic";. (Note, although the dynamic library knows what other libraries it needs, it may not know where to find them, so you'll have to arrange for them to be on the loader's library search path).

    A way round the second problem is to create what AdaCore call a standalone dynamic library, and to get it to initialize itself automatically.

    To do this you need to add two attributes:

    • for Library_Interface use (...); specifies a list of the names of the units you wish to be visible outside the library. The effect is to include only the named units' source and .ali files in the library; if the only callers are to be from C you probably only need to name one.
    • for Library_Auto_Init use "true"; - I think this is actually the default.

    I set up a little example (on Mac OS X, GNAT GPL 2014).

    Subdirectory ada

    The 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

    The 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;
    }
    

    The build

    $ 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 the execution:

    $ ./main
    Hello returned 42.
    

    To distribute your library to C users on another computer, without the Ada runtime already installed, you’d need to package up libtest.so (or .dylib, or .dll) and the Ada shared libraries required.

    On a Unix system, you’d find this out using ldd libtest.so You’re looking for libgnat*.so and libgnarl*.so. You should find these in the compiler’s object search path (usually the last line of the Object Search Path section of the output of 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)
    

    Put the shared libraries and symlinks in a directory with libtest.so, say product/, then your users should be able to link with

    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 round this is to put the libraries where the loader already looks, for example /usr/local/lib (in which case you wouldn’t need the -Lproduct).

    Another way is to tell the loader where to look by setting an environment variable (LD_LIBRARY_PATH on Linux, DYLD_LIBRARY_PATH on Mac OS X).

    A third way is to tell the linker to save the path in the executable:

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

    works on Mac OS X, probably will on Linux.

    0 讨论(0)
提交回复
热议问题