First of all, thanks for the interest in ammer
!
I talked a bit about the motivation for this project here.
I admit the language surrounding externs, libraries, native libraries, and so on can be a bit confusing or ambiguous. For the purposes of ammer
, a “native library” is a library distributed to end users as a .dll
, .so
, or .dylib
file (on Windows, Linux, and Mac OS X, respectively). Such a library is typically written in C or C++, although it really doesn’t matter – in the end it just contains machine code suitable for the target architecture.
Examples of native libraries: libpng, libuv, SDL2, zlib, and many, many more.
This is a bit different from the way things can be “native” in the context of Haxe. There are many “target native” libraries in Haxe. These libraries only work on a particular target, and maybe need Haxe externs. On Java, for example, libraries are distributed as .jar
files. In the JavaScript ecosystem, npm
hosts packages, many of which have Haxe externs.
Examples: JUnit (Java), socket.io + hxsocketio (JavaScript).
If you didn’t consider FFI mechanisms, then it would seem that native libraries are a subset of target native libraries, since something like libpng could only work on the hxcpp target.
But, as it turns out, all of Haxe system targets have some FFI mechanisms, allowing them to use native libraries if things are set up correctly. It is important to note that FFI allows using native libraries without recompiling the runtime itself (without recompiling the hl
executable, or the php
interpreter, or node
, etc). Unfortunately, “setting things up correctly” is very different from target to target, and none of our targets really support it out of the box, except hxcpp (which can only do this because it can link against native libraries at compile time like any C++ program can).
An example C library
As an example, consider a simple C library that has this function:
int add_numbers(int a, int b) {
return a + b;
}
We can compile it to a native library with a command that depends on the OS, e.g. gcc -dynamiclib -o libnative.dylib native.c
on OS X. So at this point, we have a .dll
, .so
, or .dylib
, in addition to the C headers of the library (just says int add_numbers(int, int);
in this case).
Without ammer
Now we want to use this library in HashLink. We set up the glue code:
#include <hl.h>
#include <native.h>
HL_PRIM int w_add_numbers(int a, int b) {
return add_numbers(a, b);
}
DEFINE_PRIM(_I32, w_add_numbers, _I32 _I32);
And compile with something like gcc -fPIC -o glue_native.o -c glue_native.c -I /path/to/native/include && gcc -I /path/to/native/include -D LIBHL_EXPORTS -m64 -shared -o glue_native.hdll glue_native.o -lhl -L/path/to/native/lib -lnative
. Then we make the HashLink externs:
@:hlNative("glue_native")
class Native {
public static function w_add_numbers(a:Int, b:Int):Int {
return 0; // function gets replaced after loading
}
}
And now we can call the function on HashLink with the Haxe code Native.w_add_numbers(1, 2)
.
But the above process is very different for all of our platforms, because each target has different method signature requirements, specifies FFI primitives differently, needs to compile against different libraries, etc etc. I believe this should not be the responsibility of a library maintainer, because it is error-prone, and leads to a lot of code duplication.
With ammer
With ammer
, we only need the externs in the form of a “library definition”:
class Native extends ammer.Library<"native"> {
public static function hello(a:Int, b:Int):Int;
}
We specify the paths to the library and its headers when compiling:
--library ammer
-D ammer.lib.native.include=/path/to/native/include
-D ammer.lib.native.library=/path/to/native/library
And we’re done! ammer
then takes care of creating the target-specific glue code and invoking the C compiler with the correct parameters to produce the FFI-containing dynamic library. Most importantly, any target that ammer
supports (including future additions) should work with the library definition.
TL;DR: yes, ammer
dynamically generates externs for native libraries (as in, .dll
, .so
, .dylib
) and makes such libraries available by means of FFI on any ammer
-supported target – currently HashLink, hxcpp, very soon Lua and JavaScript, then more.