I agree it's a nice idea, since node-gyp
would generate Makefiles, whereas Meson uses modern Ninja that works much faster. Also, building with node-gyp
results in warnings from node.h
you can do little about, but Meson allows to suppress them with is_system: true
.
For this answer I use an example addon from here. The C++ file:
// hello.cc
#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(
isolate, "world").ToLocalChecked());
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
} // namespace demo
The meson.build
; it would compile the addon and also test that it's functional by making node
load it then invoke hello()
from the addon:
project('C++ node example addon', 'cpp')
inc_node = include_directories('/usr/include/node', is_system: true)
# For compatibility with node-gyp we use "shared_module" rather than
# "shared_library". The difference important in the current context is that
# shared_module will happily allow undefined references at link-time. It may or may
# not be what you want, feel free to change to `shared_library`, in which case you
# have to also link it with find_library('node')
shared_module(
'hello',
['hello.cc'],
include_directories : [inc_node],
name_prefix: '', # don't prepend "lib"
name_suffix: 'node', # required for addon to load properly
)
node = find_program('node')
test('addon',
node,
args: ['-e',
'const addon = require("./hello.node");'
+ 'console.log(addon.hello());'
]
)
Here's an example of running it:
$ meson build
The Meson build system
Version: 0.62.2
Source dir: /tmp/node_C_hello_world
Build dir: /tmp/node_C_hello_world/build
Build type: native build
Project name: C++ node example addon
Project version: undefined
C++ compiler for the host machine: c++ (gcc 12.1.1 "c++ (GCC) 12.1.1 20220507 (Red Hat 12.1.1-1)")
C++ linker for the host machine: c++ ld.bfd 2.37-27
Host machine cpu family: x86_64
Host machine cpu: x86_64
Library node found: YES
Program node found: YES (/usr/bin/node)
Build targets in project: 1
Found ninja-1.10.2 at /usr/bin/ninja
$ ninja -C build/
ninja: Entering directory `build/'
[2/2] Linking target hello.node
$ ninja -C build/ test
ninja: Entering directory `build/'
[0/1] Running all tests.
1/1 addon OK 0.20s
Ok: 1
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
Full log written to /tmp/node_C_hello_world/build/meson-logs/testlog.txt
Some notes:
- the addon library has to have
.node
postfix, otherwise node
will fail to load it with a SyntaxError: Invalid or unexpected token
- the
nodejs-devel
(or whatever the package with node.h
is called on your system) package, unfortunately, lacks pkg-config .pc
files, so you have to call include_directories()
directly rather than simply using a dependency()
call.
- the
node-gyp
has some fancy arguments, which I'm not sure whether are needed or not. Am not a nodejs developer, so can't say anything useful on that matter, but here are ones I deemed to be potentially important for more complicated addons:
- link argument
-rdynamic
- compile arguments
'-DNODE_GYP_MODULE_NAME=addon' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DBUILDING_NODE_EXTENSION'