Use libsodium in NAPI - node.js

I am using a library which depends on libsodium (libpaseto). I have installed it on my machine and I am trying to build a nodejs addon.
I have the following binding.gyp file:
{
"targets": [{
"target_name": "testaddon",
"cflags!": [ "-fno-exceptions"],
"cflags_cc!": [ "-fno-exceptions" ],
"cflags_cc":["-std=c++1z" ,"-lsodium","$(pkg-config --cflags libsodium)"],
"ldflags_cc":["$(pkg-config --libs libsodium)"],
"sources": [
"cppsrc/main.cpp",
"cppsrc/functionexample.cpp",
"cppsrc/libpaseto/src/helpers.c",
"cppsrc/libpaseto/src/paseto.c",
"cppsrc/libpaseto/src/paseto_v2_local.c",
"cppsrc/libpaseto/src/paseto_v2_public.c",
],
'include_dirs': [
"<!#(node -p \"require('node-addon-api').include\")"
],
'libraries': [],
'dependencies': [
"<!(node -p \"require('node-addon-api').gyp\")"
],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ,'SODIUM_STATIC=1',
'HAVE_LIBM=1']
}]
}
Here is the code for my addon:
#include "./headers/functionexample.h"
#include <iostream>
#include <fstream>
#include "libpaseto/include/paseto.h"
#include <exception>
Napi::Boolean addon::InitPasetoWrapped(const Napi::CallbackInfo &info)
{
bool returnValue = paseto_init();
Napi::Env env = info.Env();
return Napi::Boolean::New(env, returnValue);
}
Napi::Object addon::Init(Napi::Env env, Napi::Object exports)
{
exports.Set("add", Napi::Function::New(env, addon::addWrapped));
exports.Set("getOsName", Napi::Function::New(env, addon::getOSNameWrapped));
exports.Set("writeToFile", Napi::Function::New(env, addon::writeFileWrapped));
exports.Set("initPaseto", Napi::Function::New(env, addon::InitPasetoWrapped));
return exports;
}
I am able to compile it without a problem. I am exporting some methods in my nodejs code. However when I try to run the exported method initPaseto from my js code I get the following error:
node:symbol lookup error: undefined symbol:sodium init
How can I fix it?

So I finally found the answer.In the libsodium documentation it mentions that you have to pass the -lsodium flag to be able to compile without problems.So what I had to do was to add this flag in my libraries in binding.gyp. So here is my final binding.gyp file:
{
"targets": [{
"target_name": "testaddon",
"cflags!": [ "-fno-exceptions"],
"cflags_cc!": [ "-fno-exceptions" ],
"cflags_cc":["-std=c++1z"],
"sources": [
"cppsrc/main.cpp",
"cppsrc/functionexample.cpp",
"cppsrc/libpaseto/src/helpers.c",
"cppsrc/libpaseto/src/paseto.c",
"cppsrc/libpaseto/src/paseto_v2_local.c",
"cppsrc/libpaseto/src/paseto_v2_public.c",
],
'include_dirs': [
"<!#(node -p \"require('node-addon-api').include\")"
],
'libraries': ['-lsodium'],
'dependencies': [
"<!(node -p \"require('node-addon-api').gyp\")"
],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ,'SODIUM_STATIC=1',
'HAVE_LIBM=1']
}]
}
Also this github issue helped me to understand how to add a library in my project:
https://github.com/nodejs/node-gyp/issues/328

Related

Node C++ Module Shared Library

I am trying to create a Node C++ module for the purpose of interfacing with the Steam api. The library file is ./steam/lib/linux64/libsteam_api.so, and header files are in ./steam.
I have created a small regular C++ file for testing, which successfully uses the Steam api, imported using #include "steam_api.h". I have complied and imported the shared library like this: g++ -L./steam/lib/linux64 -Wl,-rpath=./steam/lib/linux64 -Isteam -lsteam_api main.cpp
binding.gyp:
{
"targets": [ {
"target_name": "steam",
"sources": [ "steam.cpp" ],
"include_dirs": [
"steam",
"<!#(node -p \"require('node-addon-api').include\")"
],
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ],
"libraries": [ "./steam/lib/linux64/libsteam_api.so" ]
} ]
}
When I try to compile the Node module using node-gyp, I get g++: error: ./steam/lib/linux64/libsteam_api.so: No such file or directory
How do I correctly import the shared library?
After looking through some examples and a lot of trial and error, I was able to correct binding.gpy:
{
"targets": [ {
"target_name": "steam",
"sources": [ "steam.cpp" ],
"include_dirs": [
"steam",
"<!#(node -p \"require('node-addon-api').include\")"
],
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ],
"libraries": [
"-lsteam_api",
"-L../steam/lib/linux64",
"-Wl,-rpath=./steam/lib/linux64"
]
} ]
}
The libraries section needed to include the arguments similar to how they were invoked with g++, except "-L" differed from "-Wl,-rpath=" and the g++ inputs in needing to start one folder level up for some unknown reason.
It looks like node-gyp is changing the current directory as it runs, which invalidates your relative path. Either use an absolute path instead, or do some experimentation to find the new current directory and then use a path relative to that.

Visual Studio Code cannot finde g++ compiler on linux

Does anyone knows why vs-code can't find c++ compiler. I have used vc-code for several months without any problems, but suddenly without any clear reason it can't find the compiler anymore!! does somebody here can figure out what could be causing this.
tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "echo",
"type": "shell",
"command":"g++ $(pkg-config --cflags --libs opencv gl glew sdl2)",
"args": ["-g", "${workspaceFolder}/*.cpp", "-lstdc++fs", "-pthread"],
"group":{
"kind": "build",
"isDefault": true
}
}
]
}
c_cpp_properties.json
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}",
"/usr/include/c++/6.3.0",
"/usr/include/c++/6",
"/usr/include/x86_64-linux-gnu/c++/6",
"/usr/include/c++/6/backward",
"/usr/lib/gcc/x86_64-linux-gnu/6/include",
"/usr/local/include",
"/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"/usr/include/x86_64-linux-gnu",
"/usr/include",
"/usr/bin"
],
"defines": [],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"${workspaceFolder}",
"/usr/include/c++/6.3.0",
"/usr/include/c++/6",
"/usr/include/x86_64-linux-gnu/c++/6",
"/usr/include/c++/6/backward",
"/usr/lib/gcc/x86_64-linux-gnu/6/include",
"/usr/local/include",
"/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"/usr/include/x86_64-linux-gnu",
"/usr/include",
"/usr/bin"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17"
}
],
"version": 3
}
output
/bin/bash: g++ $(pkg-config --cflags --libs opencv gl glew sdl2): command not found
NOTE: I still can use g++ to compile files in the integrated terminal in vs-code, but it cannot be recognized by tasks.json !!!
i don't think that this could relate to this problem, cause i have been using it like this for long time with no problem. and in general during the compilation all of this parts gonna be clipped together.
You updated your command so your statement is not true more. Namely you added command substitution $(...) that is not processed in Visual Studio Code and passed as is in once command to bash. The right solution is below:
"tasks": [
{
"label": "echo",
"type": "process",
"command":"/bin/bash",
"args": [ "-c", "g++", "$(pkg-config --cflags --libs opencv gl glew sdl2)", "-g", "${workspaceFolder}/*.cpp", "-lstdc++fs", "-pthread"],
"group":{
"kind": "build",
"isDefault": true
}
}
]
Or bit shorter
"tasks": [
{
"label": "echo",
"type": "process",
"command":"/bin/bash",
"args": [ "-c", "g++ $(pkg-config --cflags --libs opencv gl glew sdl2) -g ${workspaceFolder}/*.cpp -lstdc++fs -pthread"],
"group":{
"kind": "build",
"isDefault": true
}
}
]

Linking against arch specific locations with node-gyp

I'm trying to build a native Node.js module that links against a 3rd party shared library. This library is delivered as part of a bundle that includes pre-built versions for different OSes and Architectures in different directories.
e.g.
/opt/Foo/linux/x86/lib/libfoo.so
/opt/Foo/linux/x86/include/foo.h
/opt/Foo/linux/x86_64/lib/libfoo.so
/opt/Foo/linux/x86_64/include/foo.h
/opt/Foo/linux/arm/lib/libfoo.so
/opt/Foo/linux/arm/include/foo.h
/opt/Foo/mac/x86_64/lib/libfoo.so
/opt/Foo/mac/x86_64/include/foo.h
my binding.gyp currently looks like this:
{
'targets': [
{
'target_name': 'foo',
'sources': ['foo.cpp', 'foo.h'],
'include_dirs': ["<!(node -e \"require('nan')\")"],
'conditions': [
['OS=="mac"', {
'include_dirs': ['/opt/Foo/mac/x86_64/include'],
'libraries': ['-L/opt/Foo/mac/x86_64/lib', '-lfoo']
}
],
['OS=="linux"', {
'include_dirs': ['/opt/Foo/linux/x86_64/include'],
'libraries': ['-L/opt/Foo/linux/x86_64/lib', '-lfoo']
}
]
]
}
]
}
I don't seem to be able to find the syntax for conditions to differentiate on the current platform architecture.
Having not found any other solutions I came up with the following:
...
['OS=="linux"', {
'include_dirs': ["<!(node -e \"console.log('/opt/Foo/linux/%s/include',require('process').arch);\")"],
'libraries': ["<!(node -e \"console.log('-L/opt/Foo/linux/%s/lib',require('process').arch);\")", '-lfoo']
}
]
...
I had to rename the /opt/Foo/Linux/x86_64 to directory to /opt/Foo/Linux/x64 to match the output from process.arch.

How to change library paths based on configuration?

I am building a native module that needs to link a static library. The path to that library. My binding.gyp file has the following appearance:
{
"targets": [
{
"target_name": "DcpServer",
"sources": [
"DcpServer.cc"
],
"include_dirs": [
"../../coratools",
"../../../boost-1.65.1"
],
"libraries": [
"<(module_root_dir)/../../coratools/release_uni64/coratools.lib"
],
"defines": [ "CSIWEB_EMBEDDED", "UNICODE", "_UNICODE" ],
"configurations": {
"Release": {
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1,
"RuntimeTypeInfo": "true"
}
}
},
"Debug": {
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1,
"RuntimeTypeInfo": "true"
}
}
}
}
}
]
}
The path to coratools.lib will vary based upon whether the debug or release configuration is selected. The problem is that node-gyp did not allow me to place the "libraries" key within the "configurations" property. Is there a way of doing what I want by making the library path conditional?
I never did discover how to do this. In the end, I switched to using cmake-js to build my native module.

node-gyp: run binding.gyp in all subdirectories

I'm developing a big node.js project which also includes several native libraries.
To use these libraries in JavaScript I'm compiling them to node addons (.node) using node-gyp.
I'd like to run node-gyp once from the root directory to compile all the available binding.gyp recursively (in all the subdirectories).
Is there any way to do that?
GYP allows to set a list of dependencies for a target. You can create a target of type: none in the top-level bindings.gyp and list there dependencies from subdirectories:
{
'targets': [
{
'target_name': 'build_all',
'type': 'none',
'dependencies': ['subdir1/bindings.gyp:*', 'subdir/subdir2/bindings.gyp:*'],
# or generate dependencies list with a command expansion
'dependencies': ['<!#(find -mindepth 2 -name binding.gyp | sed -e s/$/:*/)'],
}
]
}
This will compile all the dependencies and put them into build/ directory in the root.
For putting each addon in its corresponding directory, add a postbuild target inside the addon's binding.gyp:
{
"targets": [
{
"target_name": "my-target",
"sources": [ "example.cpp" ]
},
{
"target_name": "action_after_build",
"type": "none",
"dependencies": [ "my-target" ],
"copies": [
{
"files": [ "<(PRODUCT_DIR)/my-target.node" ],
"destination": "."
}
]
}
]
}
I didn't find any option to do this with just node-gyp, but one of the possible solutions is doing this in a script.
For example, adding the following to the package.json in the root folder:
"scripts": {
"install": "find ./app/* -name binding.gyp -execdir node-gyp rebuild ;"
}
This will cause all the native addons to compile when running npm install in the root folder.
An alternative to the other answers which seems to work so far (without ever having to update binding.gyp):
{
"targets": [
{
"target_name": "addon",
"sources": [
"<!#(node -p \"var fs=require('fs'),path=require('path'),walk=function(r){let t,e=[],n=null;try{t=fs.readdirSync(r)}catch(r){n=r.toString()}if(n)return n;var a=0;return function n(){var i=t[a++];if(!i)return e;let u=path.resolve(r,i);i=r+'/'+i;let c=fs.statSync(u);if(c&&c.isDirectory()){let r=walk(i);return e=e.concat(r),n()}return e.push(i),n()}()};walk('./sources').join(' ');\")"
]
}
]
}
(from https://stackoverflow.com/a/60947528/2016831)

Resources