创建静态库有大问题libtest.a
.
首先,Ada代码极有可能调用Ada运行时系统(RTS)。如果您创建静态库,您(或您的用户)将需要显式调用 Ada RTS,无论您是否使用gprbuild
。所以两者都不是
gcc main_c.c -ltest
nor
gprbuild -P c_main
就足够了;你会遇到这样的失败(甚至更糟):
$ 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
其次,Ada 代码可能(将会!)需要在程序启动时进行详细阐述。什么时候gprbuild
创建它添加功能的库testinit()
,你的 C 代码must在调用库的任何接口之前调用,并且testfinal()
在使用完该库后被调用(大多数人不会打扰)。
解决第一个问题的方法是创建一个动态库(.dll
在 Windows 上,.so
在 Linux 和其他 Unix 系统上,.dylib
在 Mac OS X 上)。要做到这一点,你说for Library_Kind use "dynamic";
. (请注意,虽然动态库知道它需要哪些其他库,但它可能不知道在哪里可以找到它们,因此您必须将它们安排在加载程序的库搜索路径上)。
解决第二个问题的方法是创建 AdaCore 所谓的独立动态库,并让它自动初始化。
为此,您需要添加两个属性:
-
for Library_Interface use (...);
指定您希望在库外可见的单元名称列表。效果是仅包括指定单位的来源和.ali
库中的文件;如果唯一的调用者来自 C,那么您可能只需要说出其中一个即可。
-
for Library_Auto_Init use "true";
- 我认为这实际上是默认的。
我设置了一个小示例(在 Mac OS X 上,GNAT GPL 2014)。
子目录ada
项目文件,
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;
你好,广告,
function Hello return Integer;
pragma Export (C, 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;
数量.广告,
package Number is
pragma Elaborate_Body;
Value : Integer := 0; -- before elaboration
end Number;
和号码.adb
package body Number is
begin
Value := 42; -- after elaboration
end Number;
父目录
项目文件,
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;
和main.c
#include <stdio.h>
extern int Hello(void);
int main() {
int hello = Hello();
printf("Hello returned %d.\n", hello);
return 0;
}
构建
$ 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
和执行:
$ ./main
Hello returned 42.
要将您的库分发给另一台计算机上的 C 用户,在尚未安装 Ada 运行时的情况下,您需要打包libtest.so
(or .dylib
, or .dll
)和所需的 Ada 共享库。
在 Unix 系统上,你可以使用以下命令找到这一点:ldd libtest.so
您正在寻找libgnat*.so
and libgnarl*.so
。您应该在编译器的对象搜索路径中找到它们(通常是输出的“对象搜索路径”部分的最后一行)gnatls -v
)。通常会有符号链接:
libgnat.so -> libgnat.1.so
libgnat.1.so -> libgnat.1.0.0.so
libgnat.1.0.0.so (the real thing)
将共享库和符号链接放入目录中libtest.so
, say product/
,那么您的用户应该能够链接到
gcc main.c -o main -Lproduct -ltest
or maybe
gcc main.c -o main -Lproduct -ltest -lgnat -lgnarl
根据您的操作系统,生成的可执行文件可能无法在运行时找到共享库。
解决这个问题的一种方法是将库放在加载器已经查找过的位置,例如/usr/local/lib
(在这种情况下,您不需要-Lproduct
).
另一种方法是通过设置环境变量来告诉加载程序去哪里查找(LD_LIBRARY_PATH
在Linux上,DYLD_LIBRARY_PATH
在 Mac OS X 上)。
第三种方法是告诉链接器将路径保存在可执行文件中:
gcc main.c -o main -Lproduct -ltest -lgnat -lgnarl -Wl,-rpath,$PWD/product
可以在 Mac OS X 上运行,也可能在 Linux 上运行。