我最近开始学习 Ada,知道 Ada 和 C 对象文件可以链接在一起构建多语言程序或库,是否可以使用 XS从 Perl 调用 Ada 代码?
是的!
事实上,任何可以从 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
生成hello.ali
和hello.o
; - 将
hello.ali
与gnatbind
配合使用-n
开关。这将生成包含绑定代码的b~hello.adb
和b~hello.ads
; - 将
b~hello.adb
编译为目标文件:b~hello.o
. - 将
hello.o
和b~hello.o
分组到一个包含ar
的存档中
因此,简而言之,我们将使用此生成文件:
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~*
不要忘记更新清单。
Makefile.PL
最后,MakeFile.PL 文件需要进行一些编辑。它必须调用上面的makefile来构建我们的库,然后在最终的链接阶段使用它。这是通过将MYEXTLIB
设置为src/hello.a
并在postamble
部分添加规则来完成的。
在我们的例子中,我们还需要与 libgnat 链接(对于 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()
符号不存在。检查使用 nm
工具生成MyAdaLib.so
会发现某些符号已被重命名。就我而言,它们以 _ada_
为前缀。所以我必须打电话给_ada_hello()
而不是hello()
.这可以通过src/ada.ads
使用Export
杂注进行更正:
pragma Export
(Convention => C,
Entity => hello,
External_Name => "hello" );
根据我的理解,应该对所有公共符号执行此操作,因为它确保从 C 程序中理解类型、记录等的表示。
现在,您应该能够从 XSUB 调用hello()
。享受!