如何创建一个ada lib.a并将其链接到C

8
我正在尝试创建一个Ada库,并尝试了几种不同的方法。 我已经尝试使用makefiles编译项目,并尝试从所有.o文件创建库。 这似乎没有按预期工作。 然后我向adacore支持人员寻求帮助,他们指导我使用.gpr文件来处理ada和c项目,在ada.gpr中进行设置,以创建库。这几乎起作用了,但在尝试编译ada时出现了未定义的引用。
我尝试过的方法: 命令行:
ar rc libmy_lib.a *.o

当我尝试读取库中的内容时

ld libmy_lib.a

我遇到了这个错误 ld: 警告: 找不到入口符号_start; 没有设置起始地址
项目文件: 我的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;

我的C项目文件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;

当我运行命令gprbuild c_main.gpr时,出现了两个不同的错误: 第一个是未定义的引用一些包,这些包是我的ada代码的一部分,并在gnat.adb文件中,我不知道它们存在。所以我推断是损坏的库。 第二个错误是某些包的字段无法找到/不存在,尽管代码编译正常并且可以运行。它会给出错误,说明ada代码中的字段不存在。

简而言之: 我有一个ada项目,分布在3个不同的目录中,我想从它们创建一个库。然后连接到一个C测试程序。最终我只需交付库文件。最好使用命令行。我不想处理项目文件。


1
我更新了我的问题,很抱歉我之前的尝试没有帮助。我并不认为这真的是一个错误,更多的是缺乏方向或者我自己不知道该怎么做。 - crychair
2
你可以通过在项目中添加 library project prj is 来确认它是一个库项目。 - Simon Wright
2
我刚刚删除了我的回答,因为当库需要详细说明(初始化)时,它完全失败了。如果我找到一个能够工作的答案,我会再回来的。 - Simon Wright
2
恢复了答案。简而言之:使用动态库以避免与Ada RTS库链接和扩展中出现用户可见的问题。 - Simon Wright
1个回答

10

创建静态库 libtest.a 存在较大问题。

首先,Ada代码极有可能调用Ada运行时系统(RTS)。如果您创建静态库,则必须显式地调用Ada RTS,无论您是否使用 gprbuild,而且这也适用于您的用户。因此,两者都需要进行相应的配置。

gcc main_c.c -ltest

也不

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 代码在调用库的任何接口之前必须调用该函数,并添加testfinal()函数,以在库的所有使用结束后调用(大多数人不会费心去做)。
解决第一个问题的方法是创建动态库(Windows上是.dll,Linux和其他Unix系统上是.so,Mac OS X上是.dylib)。要做到这一点,您可以使用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;

你好.ads,

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;

数字.ads,

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

和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(或.dylib.dll)和所需的Ada共享库。
在Unix系统上,您可以使用ldd libtest.so来查找这个信息。您要找的是libgnat*.solibgnarl*.so。您应该能够在编译器的对象搜索路径中找到它们(通常是gnatls -v输出的“Object Search Path”部分的最后一行)。通常会有符号链接:
libgnat.so       ->      libgnat.1.so
libgnat.1.so     ->      libgnat.1.0.0.so
libgnat.1.0.0.so         (the real thing)
将共享库和符号链接放在一个目录中,例如 product/ 目录下的 libtest.so 文件,然后您的用户应该能够使用以下方式进行链接。
gcc main.c -o main -Lproduct -ltest

或者可能

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

根据您的操作系统,生成的可执行文件可能无法在运行时找到共享库。

解决方法之一是将库放置在加载器已经查找的位置,例如/usr/local/lib(这种情况下,您不需要-Lproduct)。

另一种方法是通过设置环境变量(Linux上的LD_LIBRARY_PATH,Mac OS X上的DYLD_LIBRARY_PATH)告诉加载器要查找的位置。

第三种方式是告诉链接器将路径保存在可执行文件中:

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

该软件能在Mac OS X上使用,很可能也能在Linux上使用。


非常感谢,这对我帮助很大。还有一些要补充的事情。由于某种原因,在构建时文件名是区分大小写的。对于我的项目,我有test.1.ada和一个子程序test.helloWorld.2.ada。如果W被大写,它就找不到文件了。这很疯狂,因为ada不是区分大小写的语言,但显然gprbuild是用c编写的,因此它是区分大小写的。 - crychair
1
关于大小写敏感性 - 如果您正在创建libtest.so,则可能在Linux或BSD系统上,它们的文件系统是区分大小写的;因此,test.helloWorld.2.ada将无法看到test.helloworld.2.ada。Windows和Mac OS X保留大小写但不区分大小写。 - Simon Wright
1
回复:链接 - 添加了最后一节。我无法在这里轻松尝试。 - Simon Wright
1
抱歉在发布前没有实际尝试过。扩展了最后一部分,希望能解决您的问题。 - Simon Wright
1
我觉得这将是一个新的问题! - Simon Wright
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接