3

我最近开始学习 Ada 并且知道 Ada 和 C 目标文件可以链接在一起以构建多语言程序或库,是否可以使用 XS 从 Perl 调用 Ada 代码?

4

1 回答 1

9

是的!

事实上,任何可以从 C 调用的语言都可以在使用 XS 的 Perl 中使用。这是如何使用 Ada 模块和 ExtUtils::MakeMaker 的解决方案。

设置

模块树

让我们首先使用以下方法创建一个模块树h2xs

$ h2xs -A -n MyAdaModule

然后让我们创建一个子目录来保存我们的 Ada 文件:

$ cd MyAdaModule
$ mkdir src

这是模块的规范:src/hello.ads

procedure hello;

...和正文:src/hello.adb

with Ada.Text_IO;
use Ada.Text_IO;

procedure hello is
begin
    Put_Line("Hi from Ada!");
end;

不要忘记更新清单。

编写 XS 文件

现在让我们编写 MyAdaModule.xs 的主体。这很像使用 C 库中的函数:

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

extern void adainit();
extern void adafinal();

MODULE = MyAdaModule        PACKAGE = MyAdaModule       

void say_hello()
    CODE:
        adainit();
        hello();
        adafinal();

gnat 文档中我们知道我们需要调用adainit()adafinal()初始化然后清理。这些调用围绕hello()在此处,但它们可能会在您的 XS 文件中的某些其他函数中处于更好的位置。然后将在 Perl 模块中的 BEGIN 和 END 块中调用它们。

是时候编译了!

艾达图书馆

首先,我们不想将所有神奇的链接和绑定委托给 MakeMaker,所以让我们在 src/ 目录中创建一个 makefile,它将我们的 Ada 代码编译成一个静态库。

要制作这个库,hello.a我们只需要遵循 gnat 文档:

  • 用于gnatmake -c生成 ahello.ali和 a hello.o
  • hello.ali与开关gnatbind一起使用。-n这将生成b~hello.adbb~hello.ads包含绑定代码;
  • 编译b~hello.adb成目标文件:b~hello.o.
  • 分组hello.ob~hello.o一起归档ar

所以,简而言之,我们将使用这个makefile:

all: hello.a

hello.a: hello.o b~hello.o
    ar rcs $@ $^

hello.o: hello.adb hello.ads
    gnatmake -c -o $@ $<

b~hello.o: b~hello.adb b~hello.ads
    gnatmake -c -o $@ $<

b~hello.adb: hello.ali
    gnatbind -n $<

hello.ali: hello.o

clean:
    rm -rf *.o *.ali *.a b~*

不要忘记更新清单。

生成文件.PL

最后,MakeFile.PL 文件需要进行一些编辑。它必须调用上面的 makefile 来构建我们的库,然后在最后的链接阶段使用它。这是通过在该部分中设置和添加规则来MYEXTLIB完成的。src/hello.apostamble

在我们的例子中,我们还需要链接 libgnat (for Ada.Text_IO),它应该驻留在系统的某个位置。这是通过编辑完成的LIBS。在此示例中,路径是硬编码的,但您可能应该找出一种更便携的方法来查找 libgnat。

use 5.018001;
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
    NAME              => 'MyAdaModule',
    VERSION_FROM      => 'lib/MyAdaModule.pm', # finds $VERSION
    PREREQ_PM         => {}, # e.g., Module::Name => 1.1
    ($] >= 5.005 ?           # Add these new keywords supported since 5.005
      (ABSTRACT_FROM  => 'lib/MyAdaModule.pm', # retrieve abstract from module
       AUTHOR         => 'A. U. Thor <author@nonet>') : ()),
    DEFINE            => '',    # e.g., '-DHAVE_SOMETHING'
    INC               => '-I.', # e.g., '-I. -I/usr/include/other'
    LIBS              => ['-L/usr/lib/gcc/i686-pc-linux-gnu/4.8.2/adalib/ -lgnat'],
    MYEXTLIB          => 'src/hello.a',
);


sub MY::postamble {
    join("\n",
        "\$(MYEXTLIB)::",
        "\tmake -C src/",
        "",
        "clean::",
        "\tmake -C src/ clean",
    );
}

现在试试

$ perl Makefile.PL
$ make
$ make test

惊喜:测试没有通过!hello()符号不存在。使用该工具检查MyAdaLib.somake 生成的nm符号会发现某些符号已被重命名。就我而言,它们的前缀是_ada_. 所以我不得不打电话_ada_hello()而不是hello(). 这可以通过src/ada.ads使用Export编译指示来纠正:

pragma Export
(Convention    => C,
 Entity        => hello,
 External_Name => "hello" );

据我了解,应该对所有公共符号执行此操作,因为它可以确保从 C 程序中理解类型、记录等的表示。

现在,您应该可以hello()从 XSUB 调用了。享受!

于 2013-11-22T07:25:03.287 回答