如何创建ada lib.a并链接到C

时间:2014-11-07 16:18:26

标签: c ada gnat gnat-gps

我正在尝试创建一个ada库并尝试了一些不同的东西。 我尝试使用makefile编译项目并尝试从所有.o文件创建一个库 这似乎没有按预期工作。 然后我问了adacore的支持,他们指出了我为ada和c项目使用.gpr文件的方向,以及应该创建一个库的ada.gpr文件。这几乎起作用,但是当它试图编译ada时,我得到了未定义的引用。

我尝试过: 命令行:

ar rc libmy_lib.a *.o

当我尝试阅读lib中的内容时

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

我有2个不同的错误: 首先是未定义的引用,这些包是我的ada代码的一部分,来自gnat .adb文件,我不知道存在。我这么破坏的图书馆。 其次是某些包的字段无法找到/不存在,即使代码编译正常并运行。它给出了错误,指出字段在ada代码中不存在。

TLDR: 我在3个不同的目录中有一个ada项目,我想从它们创建一个库。然后链接到C测试程序。最终我只需要提供库文件。命令行是最好的。我不想处理项目文件。

1 个答案:

答案 0 :(得分:7)

创建静态库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调用standalone dynamic library的内容,并让它自动初始化。

要执行此操作,您需要添加两个属性:

  • 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;

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;

和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输出的对象搜索路径部分的最后一行)。通常会有符号链接:

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

将共享库和符号链接放在包含libtest.so的目录中,比如product/,然后您的用户就可以链接

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。