How to Create an NME Extension

One of the things that I love about NME is that it is easy to extend. Unfortunately, there is not much documentation on the subject, so I want to take a few minutes to help describe what you can do with an NME extension, and how to create one. Trust me, it is not difficult.

include.nmml

Beginning with NME 3.2, all you need to make an extension is add a file called “include.nmml.” This file is processed just like an ordinary NMML project file, with the exception that paths are relative to where your extension is located.

For example, in the “Test” extension I have created, this is my “include.nmml” file:

<?xml version="1.0" encoding="utf-8"?>
<project>
 
	<ndll name="test" />
	<java path="project/android" />
 
</project>

You could include assets, defines, haxelib dependencies, NDLL references, Java code… practically anything you can define from an ordinary NMML project file for NME, you can define using your extension.

This means that an extension does not necessarily have to call native code. Maybe you have made a game framework in haxe, but would like to include some additional asset files automatically, or set a few defines so that users can add conditional code like “#if cool_game_framework” in their haxe code. Just add an “include.nmml” with your library.

Using Extensions

So long as your extension has an “include.nmml” file, there are two ways that users can include your extension.

If the extension is used locally, you can use an <include /> tag:

<include path="path/to/extension" />

When you publish an extension to haxelib, users only need a <haxelib /> tag:

<haxelib name="my-extension" />

Calling Java Code

If there is a <java /> node in your project or extension NMML file, those classes will be compiled into an Android application.

In order to reference classes that are included with NME, a part of Android or one of the custom classes you specify to include, you will need to access the Java Native Interface, or JNI.

Although you can make JNI bindings in C++ code, NME provides a simpler way, using the nme.JNI class. You can create bindings to static or member objects in Java, without leaving Haxe. Although I do not have an example for you right now, NME does include a “generate” command which can even create Haxe classes automatically, based on Java code. It will read compiled Java files, and generate Haxe classes with the appropriate JNI bindings automatically.

Here is an example from the “Test” extension:

if (jni_get_name == null) {
 
	jni_get_name = JNI.createStaticMethod ("Test", "getName", "()Ljava/lang/String;");
 
}
 
platforms.push (jni_get_name ());

In this sample, I am checking to see if I have already created a JNI binding for my static “Test.getName” method in Java. The signature of the method accepts no arguments, but returns an object of type “java.lang.String”

Oracle has further documentation on the syntax for JNI bindings. Remember that classes are separated by slashes, not periods, so if I was calling “my.extension.Test” in Java, I would want to have “my/extension/Test” as the first argument, above.

With the binding created, I can call it like a normal method. Using a slightly different approach, we can also do the same with native C-based code.

Calling C, C++ or Objective-C Code

Haxe includes a standard interface for using native code, called CFFI.

The basic idea is that you call “Lib.load” in your Haxe classes, and you will be able to connect to a native method. When you create your own extensions, your code is compiled into its own “NDLL” library, which varies slightly in name and format depending on the platform you compile for.

Each of the methods you want to call from Haxe needs to be exposed, using a method in C called DEFINE_PRIM. This handles the marshaling between your Haxe code and your native code. In NME, there is a file called “ExternalInterface.cpp” which handles this “glue” layer for NME’s native methods. Although you could put this code in any of your C source files, I have also followed the “ExternalInterface.cpp” convention in order to mirror NME’s own structure.

For a simple native call, you may decide to put the response or action in this “glue” method. However, for anything larger, you probably will want to keep the “glue” code light, and call into other classes you’ve defined in order to handle the real work. This also makes it easier to abstract calls between different platforms, if that is required. You could have different “System.cpp” files for each platform, for example, or perhaps you will need to define the method in Objective-C for iOS, but will define it in C++ for other targets.

Here is an example from the “Test” extension:

platforms.push (cpp_get_name ());
 
...
 
private static var cpp_get_name = Lib.load ("test", "test_get_name", 0);

I can define a method which calls native code, by using “Lib.load”. The first argument is the name of the library I wish to use. For my “Test” extension, I created an NDLL called “test”. The second argument is the name of the method I want to call. In a moment I’ll show what the C code looks like, which exposes this call. The last argument is the number of parameters I should use when calling this method. For this example, I used a method that expects no arguments.

Here is what it looks like on the other side, in my native code:

static value test_get_name () {
 
        return alloc_string (GetName ());
 
}
DEFINE_PRIM (test_get_name, 0);

You can see that this is pretty straightforward. In Haxe I can get a reference to my native method, using the name and the number of arguments. You can see above that I specify the same in C/C++.

Here is what the “GetName” method looks like:

const char* GetName () {
 
        return "C++";
 
}

There are methods which help convert generic “value” objects into C data types, and vice versa. The “GetName” method returns a simple “const char*” value. When this value is returned to Haxe, I am using the “alloc_string” method. This converts a C/C++ string into a Haxe string. On the reverse, if I had received a string from Haxe, I would be able to use “val_string” to marshal the incoming value as a C/C++ string.

Example Extension

You can find the code to the “Test” extension (as well as a simple project that uses it) at http://code.google.com/p/nme-extensions.

Building the Example

There are two steps to building the example. Since it includes native C++ code, you will want to compile that for your target platform before compiling the application.

HXCPP includes a build tool which simplifies the process of compiling native code for each target platform. The “Test” extension includes a Build.xml file which you can customize if you need to include more C/C++/Objective-C files, additional flags, or conditional statements to help control which files and settings are applied to each platform you target.

Go to the “Extension/project” directory using a command-prompt or terminal, then use one of the following commands to use HXCPP to compile the code for each platform you want:

haxelib run hxcpp Build.xml
haxelib run hxcpp Build.xml -DHXCPP_M64
haxelib run hxcpp Build.xml -Diphoneos
haxelib run hxcpp Build.xml -Diphoneos -DHXCPP_ARMV7
haxelib run hxcpp Build.xml -Diphonesim
haxelib run hxcpp Build.xml -Dwebos
haxelib run hxcpp Build.xml -Dandroid
haxelib run hxcpp Build.xml -Dblackberry

The first command will build for your current desktop platform. Adding “HXCPP_M64” will compile for 64-bit, which is valuable for 64-bit Linux builds. The new “HXCPP_ARMV7” define will compile an armv7 build for iOS, instead of armv6. Those only available in the new version of HXCPP that is included in the NME 3.3 release candidate, or that was just posted on haxelib this morning.

The rest should be fairly straightforward — iOS simulator, webOS, Android or BlackBerry.

When you have compiled the native library, you can move on to test the example project.

  • Dion Whitehead Amago

    Thanks so much for this Joshua.  This helps out a *lot*.

  • Philippe

    And now, we need a primer on how to use the GC 😉

  • FYI, I just updated the “Test” extension in order to support BlackBerry and armv7 iOS builds, which were added in the recent update to HXCPP

  • Oleg P

    Someone please help me. I’m getting an compiler error(after executing haxelib run hxcpp Build.xml):
    Test.obj : error LNK2019: unresolved external symbol __imp__mouse_event@20 refer
    enced in function “int __cdecl test::TwoPlusTwo(void)” (?TwoPlusTwo@test@@YAHXZ)

    when I’m using this code in my c++ extension:
    #include
    #include
    #include
    int TwoPlusTwo () {
      mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );
      mouse_event( MOUSEEVENTF_LEFTUP , 0, 0, 0, 0 );   return 2 + 2;
    }

    I cant use functions such as SetCursorPos(x,y) or mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0) ?
    what am I doing wrong?

  • le

    is it possible make a box2d c++ extension?

  • Joshua, thanks again for all your efforts to make NME an even greater tool 🙂

  • Thank you for this!

  • Yes, that would be possible. I already did some work myself to walk out how that might work

  • It sounds like…

    When you use “mouse_event”, it compiles because you are dynamically linking to a system library. When HXCPP loads your extension, it fails to load because these symbols are not found. You can either try to statically link these symbols, or your final application may have to be linked to the same libraries (‘-lNameOfLibrary”) at runtime?

  • allanbishop

    I was oblivious to the fact that we can create extensions and their uses. This really makes me excited to be developing with NME 🙂

  • allanbishop

    Have you worked anymore on this? I would be interested to help out since the Box2D C++ version is up to 2.2.1 and has some cool things in it. 

  • Nathan

    First of all, great article. Works like a charm. I am now trying to extend it but I don’t know how to reference an external static .lib file. Everything compiles nicely in Visual Studio when I link it using the IDE but when I try to compile using the haxe build tool I get an unresolved external reference.

    I’m guessing there is some tag in the build.xml file but nothing I’ve tried seems to work.

  • I think you will want to add something like this in the Build.xml file, inside the node:

    Unfortunately, the way to use and configure a HXCPP XML file is not well documented, but I can usually figure it out by looking at the Build.xml that’s included with NME.

  • Nathan

    Thanks, that works.  Now on to my next compilation error…

  • cambiata

    Thank you, Joshua!

    Just tried this on Win Haxe 2.09 NME 3,3,3, and it works after commenting out the nme/JNI.hx line
    typedef JNI = neash.JNI; (around line 30)
    as described here: http://www.haxenme.org/community/forums/bugs/nme-3.3.2-windows-extension-build-problem-caused-by-jni/

    / Jonas

  • Guest

    Any chance you can upload the example again?

  • zzzzzzerg

    Thank you, it helps me a lot!

  • EricZhao

    When I run haxelib run hxcpp build.xml,I get the problem bellow:

    <ject ericzhao$ haxelib run hxcpp Build.xml -Diphonesim

    mkdir obj

    mkdir obj/iphonesim

    mkdir obj/iphonesim/common

    /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/g++ -Iinclude -c -O2 -fmessage-length=0 -pipe -Wno-trigraphs -fno-stack-protector -fpascal-strings -fasm-blocks -Wreturn-type -Wunused-variable -arch i386 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk -fvisibility=hidden -miphoneos-version-min=4.2 -DIPHONE -DIPHONESIM -DSTATIC_LINK -I/usr/lib/haxe/lib/hxcpp/2,10//include -x c++ -fvisibility-inlines-hidden -frtti ./common/ExternalInterface.cpp -oobj/iphonesim//common/ExternalInterface.o

    sh: /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/g++: No such file or directory

    Called from ? line 1

    Called from BuildTool.hx line 1265

    Called from BuildTool.hx line 554

    Called from a C function

    Called from BuildTool.hx line 591

    Called from BuildTool.hx line 710

    Called from BuildTool.hx line 744

    Called from BuildTool.hx line 200

    Uncaught exception – Error : 32512 – build cancelled

    ////////////////////////////////////////////////////////////////

    you can get the test project sample for nme-extension from http://code.google.com/p/nme-extensions/source/checkout

    except the nme-extension,I tried nmex library,and get the same problem.Who know any other way to call iOS and android ad interface?I want to make a game with an ad in it.Thank you!

  • EricZhao

    When I run haxelib run hxcpp build.xml,I get the problem bellow:

    /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/g++ -Iinclude -c -O2 -fmessage-length=0 -pipe -Wno-trigraphs -fno-stack-protector -fpascal-strings -fasm-blocks -Wreturn-type -Wunused-variable -arch i386 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk -fvisibility=hidden -miphoneos-version-min=4.2 -DIPHONE -DIPHONESIM -DSTATIC_LINK -I/usr/lib/haxe/lib/hxcpp/2,10//include -x c++ -fvisibility-inlines-hidden -frtti ./common/ExternalInterface.cpp -oobj/iphonesim//common/ExternalInterface.o

    sh: /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/g++: No such file or directory

    Called from ? line 1

    Called from BuildTool.hx line 1265

    Called from BuildTool.hx line 554

    Called from a C function

    Called from BuildTool.hx line 591

    Called from BuildTool.hx line 710

    Called from BuildTool.hx line 744

    Called from BuildTool.hx line 200

    Uncaught exception – Error : 32512 – build cancelled

  • volzhs

    How can I use default app package path in include.nmml like below.

    when test it, ::PACKAGE_PATH:: is just remained as “::PACKAGE_PATH::”, not converting to value.

    or… is there other way to use custom MainActivity.java instead of default MainActivity.java class?

    Thanks for great document.

  • Jaume Mayor

    I have also the same question!

  • hopewise

    Is there an updated post to build the extension using openFL, I am on Win7, I successfully compiled the extension, but couldn’t run the test example project.

    Here is what I get:
    haxelib run nme build “C:xampp1.8.1htdocsdcaclab_html5extesion2TestProjectExtension Test.nmml” windows
    Library nme is not installed

    So, I renamed Extension Test.nmml to Extension Test.xml to use openFL, here is what I get:

    Building Extension Test
    haxelib run openfl build “C:xampp1.8.1htdocsdcaclab_html5extesion2TestProjectExtension Test.xml” windows
    Called from CommandLineTools.hx line 668
    Called from /usr/lib/haxe/std/neko/_std/sys/io/Process.hx line 88
    Called from /usr/lib/haxe/std/neko/_std/sys/io/Process.hx line 96
    Uncaught exception – Process creation failure : C:Motion-Twinhaxe/haxelib
    Build halted with errors (haxelib.exe).

    Any help?

  • Pier

    404