我最近开始学习 Ada 并且知道 Ada 和 C 目标文件可以链接在一起以构建多语言程序或库,是否可以使用 XS 从 Perl 调用 Ada 代码?
1 回答
是的!
事实上,任何可以从 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
和 ahello.o
; hello.ali
与开关gnatbind
一起使用。-n
这将生成b~hello.adb
并b~hello.ads
包含绑定代码;- 编译
b~hello.adb
成目标文件:b~hello.o
. - 分组
hello.o
并b~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.a
postamble
在我们的例子中,我们还需要链接 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.so
make 生成的nm
符号会发现某些符号已被重命名。就我而言,它们的前缀是_ada_
. 所以我不得不打电话_ada_hello()
而不是hello()
. 这可以通过src/ada.ads
使用Export
编译指示来纠正:
pragma Export
(Convention => C,
Entity => hello,
External_Name => "hello" );
据我了解,应该对所有公共符号执行此操作,因为它可以确保从 C 程序中理解类型、记录等的表示。
现在,您应该可以hello()
从 XSUB 调用了。享受!