Yes!
In fact, any language that can be called from C can be used from Perl using XS. Here's a solution to how to do it with an Ada module and ExtUtils::MakeMaker.
Setting things up
Module tree
Let's start by creating a module tree using h2xs
:
$ h2xs -A -n MyAdaModule
Then let's create a subdirectory to hold our Ada files:
$ cd MyAdaModule
$ mkdir src
Here is the module's specification: src/hello.ads
procedure hello;
... and the body: src/hello.adb
with Ada.Text_IO;
use Ada.Text_IO;
procedure hello is
begin
Put_Line("Hi from Ada!");
end;
Don't forget to update the MANIFEST.
Writing the XS file
Let's write the body of MyAdaModule.xs now. It's pretty much like using a function from a C library:
#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();
From the gnat documentation we know that we need to call adainit()
and adafinal()
to initialise and then clean up. These calls surround hello()
here but they would probably be in a better place in some other function in your XS file. They would then be called from a BEGIN and END block in your Perl module.
Time to compile!
Ada library
First, we don't want to delegate all the magic linking and binding to MakeMaker so let's create a makefile in the src/ directory that will compile our Ada code into a static library.
To make this library, hello.a
, we just have to follow the gnat documentation:
- use
gnatmake -c
to generate ahello.ali
and ahello.o
; - use
hello.ali
withgnatbind
with the-n
switch. This will generateb~hello.adb
andb~hello.ads
which contain binding code; - compile
b~hello.adb
into an object file:b~hello.o
. - group
hello.o
andb~hello.o
together into an archive withar
So, in short, we will use this 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~*
Don't forget to update the MANIFEST.
Makefile.PL
Finally, the MakeFile.PL file needs some editing. It has to call the above makefile to build our library and then use it in the final linking phase. This is done by setting MYEXTLIB
to src/hello.a
and by adding a rule in the postamble
section.
In our case, we also need to link with libgnat (for Ada.Text_IO
) which should reside somewhere on your system. This is done by editing LIBS
. In this example the path is hardcoded but you should probably figure out a more portable way to find 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",
);
}
Now try
$ perl Makefile.PL
$ make
$ make test
And surprise: the test doesn't pass! The hello()
symbol doesn't exist. Inspecting the MyAdaLib.so
generated by make with the nm
tool reveals that some symbols have been renamed. In my case, they were prefixed with _ada_
. So I'd have to call _ada_hello()
instead of hello()
. This can be corrected in src/ada.ads
using the Export
pragma:
pragma Export
(Convention => C,
Entity => hello,
External_Name => "hello" );
From what I understood, this should be done for all public symbols as it ensures the representation of types, records, etc, is understood from a C program.
Now, you should be able to call hello()
from the XSUB. Enjoy!