-
Notifications
You must be signed in to change notification settings - Fork 80
Nontrivial Addons
Building native nodejs addons for Android is a little tricky. I will walk through building a complicated (and very useful) node addon for Android, sqlite3. Although this specific guide is used for sqlite3, many of the learnings can be applied to other addons as well.
Also, follow the steps taken at skabbes/anode-addon-example
Obtaining the code view diff
Grab a copy of node-sqlite3 to start with
git clone git://github.com/developmentseed/node-sqlite3.git
Pull in the alternative sqlite3 sources view diff
Download the amalgamation sources from the sqlite website, and extract it into node-sqlite3/deps.
Copy in Makefiles from anode sample addon code view diff
Sample files of both Android.mk and Application.mk are available in anode/sdk/addon/sample/ start. These are good starting points for writing the Android makefiles
Modifying Android.mk and Application.mk for SQLite view diff
The Application.mk is very simple and should be self explanatory, except one part APP_STL.
Since the sqlite3 bindings make use of the STL, but the android version of it is rather crippled, you will need to link in your own version. Thankfully, Android gives you several choices.
-
system
-> Use the default minimal system C++ runtime library. -
gabi++_static
-> Use the GAbi++ runtime as a static library. -
gabi++_shared
-> Use the GAbi++ runtime as a shared library. -
stlport_static
-> Use the STLport runtime as a static library. -
stlport_shared
-> Use the STLport runtime as a shared library. -
gnustl_static
-> Use the GNU STL as a static library. -
gnustl_shared
-> Use the GNU STL as a shared library.
The Android.mk file is a bit more involved, this particular version builds 2 separate modules, sqlite3 as a shared library, and sqlite3 bindings for node. These 2 portions are pretty obviously separated in the repo.
You must also supply the cppflags and cflags to the compiler (if any are necessary). You will notice custom cpp flags for building the module, these are taken right out of the addon's wscript file. You will notice in node-sqlite3 a function called build which defines the compiler flags to use. This will be very similar for other addons.
def build(bld):
obj = bld.new_task_gen("cxx", "shlib", "node_addon")
build_internal_sqlite3(bld)
obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE",
"-DSQLITE_ENABLE_RTREE=1", "-pthread", "-Wall"]
# snip ...
At this point, you are ready to build. Since we didn't follow the "normal" Android conventions for layout out our project directory, we need to specify a couple of variables to ndk-build
ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk
Ensure that ndk-build (from the Android NDK) is on your build path. Running the above command will produce three files in libs/armeabi/. Hold on to these, you will need them later.
- libgnustl_shared.so
- libsqlite3.so
- sqlite3_bindings.node
Loading the correct version of sqlite3 _ bindings from Javascript view diff
Next, we need to ensure that the Javascript portions of the node module actually use the new built version of the addon. We will change this
// Original version
var sqlite3 = module.exports = exports = require('./sqlite3_bindings.node');
to this
// Android Version
var sqlite3 = module.exports = exports = require('sqlite3_bindings.node');
Fix uv_default_loop() bugs view diff
At this time, you would expect everything to work, but unfortunately there are bugs in practically every single node addon which uses uv_queue_work
(include sqlite3). Instead of uv_default_loop()
calls, addons should use the node v0.7.x api node::Isolate::Loop()
. Since anode doesn't support v0.7.x (yet) we must replace every call to uv_default_loop()
with our patched version of node::Isolate::Loop()
. Without making this change, your addon will appear to load, but won't actually do any work.
/*
* Add a compatible function call to the 'correct' way to determine the
* current loop in node 0.7. This is essential if you are using Isolates.
*
* This should be removed once the addon is updated for at least node 0.7.x
*/
uv_loop_t * Loop(){
#if defined(HAVE_ISOLATES) && HAVE_ISOLATES
return node::Isolate::GetCurrent()->GetLoop();
#else
return uv_default_loop();
#endif
}
All the changes in the code should still compile just fine, plus we need new libraries since we changed code.
ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk
In order to use the libraries, you will need to place them into /data/data/com.yourdomain.android/node_modules
and also System.load
them. Anode already does this process for libjninode.so and bridge.node in RuntimeNative.java.
// IN THIS ORDER
System.load("/path/on/device/to/libgnustl_shared.so");
System.load("/path/on/device/to/libsqlite3.so");
System.load("/data/data/com.yourdomain.android/node_modules/sqlite3_bindings.node");
Congrats, you can now use node-sqlite3 bindings on Android. I have tested this, and it is compatible with Android's sqlite implementation as well.