I am creating a C++ addon and I wanted to use a static library. i have a .a library libarith which does simple addition.
-libarith.a
-hello.cc
-hello.js
my binding.gyp file is as follows:-
{ "targets": [
{
"target_name": "addon",
"sources": [ "hello.cc" ],
"libraries": [ "-/home/folder/api/addon/libarith.a" ],
"cflags!": [ "-fno-exceptions" ],
"cflags": [ "-std=c++11" ],
"cflags_cc!": [ "-fno-exceptions" ]
}
]
}
When i compile my hello.cc it compiles well. But when i run my addon , it gives following error:
node: symbol lookup error: /home/folder/api/addon/build/Release/addon.node: undefined symbol: _ZN4demo3sumEii.
I am new to addons, a help would be very much appreciated.
Code Snippet:-
libarith.a contains:
int sum(int a ,int b){
return a+b;
}
// hello.cc
#include <node.h>
#include <vector>
#include <iostream>
#include <v8.h>
using namespace std;
using namespace v8;
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
void newmethod(const FunctionCallbackInfo<Value>& args)
{ extern int sum(int,int);
Isolate* isolate = args.GetIsolate();
double abc= sum(4,5);
Local<Number> newnumber =Number::New(isolate,abc);
v8::String::Utf8Value r(args[1]);
std::string rst(*r);
Local<String> first = String::NewFromUtf8(isolate, "firstargument");
Local<String> second = String::NewFromUtf8(isolate, "secondargument");
Local<Object> newobj= Object::New(isolate);
newobj->Set(first,String::NewFromUtf8(isolate, *s));
newobj->Set(second,newnumber);
args.GetReturnValue().Set(newobj);
}
void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "newmethod", newmethod);
}
NODE_MODULE(addon, init)
}
//hello.js
const addon = require('./build/Release/addon');
var ss = "helloo";
var samplestring = "It is not a sample string";
console.log(addon.newmethod(samplestring, ss));
EDIT:- The solution worked as follows. I tried to create a separate directory for the libraries and it worked fine.
It says, it cannot find the declaration(implementation of .h). I think you give wrong direction to your library. There are two solutions:
Write full directory to your library at binding.gyp ~/whereItIsLocated, in my case it was ~/CLionProjects/node-player-core/PlayerCore/lib/x64/yourLibraryName.a
If previous solution couldn't help, you can copy you library to /usr/lib. You can do it with sudo cp ~/whereLibraryLocated /usr/lib.
Specify the .so library used for appropriate .h file in the binding.gyp file helps me to solve same issue:
"libraries": [
"/usr/lib/mylib.so"
]
This links help me to found it: Getting "Symbol Lookup Error" when calling C library from C++ (Node.js Addon)
Related
I am trying to use pcap(libpcap library in Linux) in Electron through NodeJS's node_api.
The key code block in C++ is followed.
static napi_value RunCallback(napi_env env, const napi_callback_info info)
{
char errBuf[PCAP_ERRBUF_SIZE];
//network device name
char *devName = "wlo1";
pcap_t *device = pcap_open_live(devName, 65535, 1, 0, errBuf);
.......
}
The binding.gyp:
{
"targets":[
{
"target_name":"net",
"sources": [ "./code/net.cpp" ],
"libraries": [
"-lpcap"
],
}
]
}
The log is here:
> my-electron-app#1.0.0 start
> electron .
Error: wlo1: You don't have permission to capture on that device (socket: 不允许的操作)
I tried using sudo -E npm start, but it didn't work for me.
However I can simply run code like pcap_open_live() in a single file compiled by g++, like sudo ./test. So I wonder if there is any setting I should do in Electron or Node.JS, like in binding.gyp or js files?
I am experiencing an issue with CLang's libastmatchers while working with Postgres sources: it can't find include file. This error is reproduced only when CLangTool is created from two files. If it is created for a separate file, there is no error and function is successfully matched.
Full error:
/home/myuser/postgres/src/conditional.c:1:10: fatal error: 'pg_config_ext.h' file not found
#include "pg_config_ext.h"
^~~~~~~~~~~~~~~~~
1 error generated.
Error while processing /home/myuser/postgres/src/conditional.c.
Directory structure:
postgres/
|-- compile_commands.json
|-- pg_config_ext.h
`-- src
|-- backend
| `-- nodeHash.c
`-- conditional.c
File contents:
nodeHash.c is empty;
conditional.c:
#include "pg_config_ext.h"
pg_config_ext.h:
int f();
compile_commands.json:
[ {
"arguments": [
"clang",
"-c",
"-I..",
"/home/myuser/postgres/src/conditional.c"
],
"directory": "/home/myuser/postgres/src",
"file": "/home/myuser/postgres/src/conditional.c"
},
{
"arguments": [
"clang",
"-c",
"-I../..",
"/home/myuser/postgres/src/backend/nodeHash.c"
],
"directory": "/home/myuser/postgres/src/backend",
"file": "/home/myuser/postgres/src/backend/nodeHash.c"
}
]
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(untitled)
set(CMAKE_CXX_STANDARD 17)
find_package(Clang REQUIRED)
include_directories(${CLANG_INCLUDE_DIRS})
add_definitions(${CLANG_DEFINITIONS})
add_executable(clang_error main.cpp)
target_link_libraries(clang_error PUBLIC clangTooling clangBasic clangASTMatchers)
main.cpp:
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Tooling/Tooling.h>
#include <string>
#include <vector>
#include <memory>
#include <iostream>
using namespace clang::ast_matchers;
using namespace clang;
using namespace clang::tooling;
using namespace std;
string prefix = "/home/myuser/";
string ccPath = prefix + "postgres/";
vector<string> files = {
prefix + "postgres/src/backend/nodeHash.c", // if you comment out this line, there will be no error
prefix + "postgres/src/conditional.c"
};
class Fetcher : public MatchFinder::MatchCallback {
public:
void run(const MatchFinder::MatchResult &Result) override {
if (const auto *FS = Result.Nodes.getNodeAs<FunctionDecl>("function")) {
std::cout << "Matched"; // Matches only if one file, not two
}
}
};
int main() {
unique_ptr<clang::tooling::ClangTool> clangTool;
unique_ptr<Fetcher> fetcherInstance;
MatchFinder finder;
static const DeclarationMatcher functionMatcher = functionDecl().bind("function");
string errMsg;
shared_ptr<clang::tooling::CompilationDatabase> cDb = clang::tooling::CompilationDatabase::autoDetectFromDirectory(ccPath, errMsg);
std::cout << errMsg; // No output
clangTool = std::make_unique<ClangTool>(*cDb, files);
fetcherInstance = std::make_unique<Fetcher>();
finder.addMatcher(functionMatcher, fetcherInstance.get());
clangTool->run(newFrontendActionFactory(&finder).get());
}
I am using CLang version 10.0.0
Perhaps, this is the same issue, but there is no answer.
I'm new in developing nodejs addons in C++.
I have a static c lib that I include in my addon, the lib is needed to talk to a custom hardware, but some functions of this library uses math.h. So when I compile in a C example I do: gcc main.c libmackrnp6_0_2_fPIC.a -o test -lm, no problem there, but when I include this lib in the cpp addon I have a problem with the pow function.
How can I compile the addon sudo node-gyp configure build with the -lm option (gcc equivalent)?
binding.gyp
{
"targets": [
{
"target_name": "octotuner",
"sources": [
"params.cpp",
"headerv6.h"
],
"libraries": ["/home/nvidia/webrf/api/libmackrnp6_0_2_fPIC.a"],
"link_settings": {
"libraries": [
"-lm"
]
},
}
]
}
params.cpp
#include <node.h>
#include <iostream>
#include <stdio.h>
extern "C" {
#include "headerV6.h"
}
using namespace v8;
using namespace std;
void getParams(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
int status = 0, numplaca = 0;
unsigned char json[9999] = ""; //<-- the lib function needs this (I know)
unsigned char flagTuner = 0;
int timeOut = 20; //segundos
parametros(json, flagTuner, numplaca, timeOut, &status); // <-- this uses pow
std::string sJson(reinterpret_cast<char*>(json));
Local<String> retval = String::NewFromUtf8(isolate, sJson.c_str());
args.GetReturnValue().Set(retval);
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "getRF", getParams);
}
NODE_MODULE(octotuner, Initialize);
teste.js
const rf = require('./build/Release/octotuner');
console.log(rf.getRF())
Testing
sudo node teste.js
Output:
{
"Numero_da_Placa":0,
"Comando_OK":1,
"Tuner0":{
"Canal_Fisico":25,
"QUALIDADE_LA":"-",
"QUALIDADE_LB":"-",
"QUALIDADE_LC":"-",
"BER_LA":"-",
"BER_LB":"-",
"BER_LC":"-",
"Potencia":-1.95,
"SNR":3.9,
"Modo":"8K",
"Intervalo_de_Guarda":"1/8",
"Modulacao_LA":"QPSK",
"Taxa_do_Codigo_LA":"2/3",
"Entrelacamento_LA":400,
"Segmentos_LA":1,
"Modulacao_LB":"64-QAM",
"Taxa_do_Codigo_LB":"3/4",
"Entrelacamento_LB":200,
"Segmentos_LB":3,
"Modulacao_LC":"Error",
"Taxa_do_Codigo_LC":"Error",
"Entrelacamento_LC":"Error",
"Segmentos_LC":"Error"
}
}
C program that uses the same library (not in addon, just for testing):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "headerV6.h"
int main(int argc, char *argv[]){
int i = 0, status = 0, numplaca = 0;
char json[9999] = {0};
unsigned char flagTuner = 0;
int timeOut = 20; //segundos
parametros(json, flagTuner, numplaca, timeOut, &status);
printf("%s", json);
return 0;
}
Compiled with: gcc params.c libmackrnp6_0_2_fPIC.a -o teste
Compile FAIL (that's why I think the problem with the addon is that it's not linking math lib)
(metadados.o): na função `recoverDataNum':
metadados.c:(.text+0x138): referência indefinida para `pow'
metadados.c:(.text+0x1d8): referência indefinida para `pow'
collect2: error: ld returned 1 exit status
Compiled with: gcc params.c libmackrnp6_0_2_fPIC.a -o teste -lm
Compile OK
Testing:
sudo ./teste
{
"Numero_da_Placa":0,
"Comando_OK":1,
"Tuner0":{
"Canal_Fisico":25,
"QUALIDADE_LA":100.0,
"QUALIDADE_LB":100.0,
"QUALIDADE_LC":"-",
"BER_LA":0.0000e+00,
"BER_LB":0.0000e+00,
"BER_LC":"-",
"Potencia":-19.50,
"SNR":37.9,
"Modo":"8K",
"Intervalo_de_Guarda":"1/8",
"Modulacao_LA":"QPSK",
"Taxa_do_Codigo_LA":"2/3",
"Entrelacamento_LA":400,
"Segmentos_LA":1,
"Modulacao_LB":"64-QAM",
"Taxa_do_Codigo_LB":"3/4",
"Entrelacamento_LB":200,
"Segmentos_LB":12,
"Modulacao_LC":"-",
"Taxa_do_Codigo_LC":"-",
"Entrelacamento_LC":"-",
"Segmentos_LC":"-"
}
}
Solved
I've used the following binding.gyp and I've got a new version of the thirdy part library and it's working now! Thanks.
{
"targets": [
{
"target_name": "octotuner",
"sources": [
"params.cpp"
],
"include_dirs": [
"./so"
],
"link_settings": {
"libraries": ["/home/nvidia/webrf/api/so/libmackrnp6_0_2_fPIC.a","-lm"],
}
}
]
}
In electron, I want to add a .cpp target, that uses OpenCv and it's VideoCapture.
My binding.gyp looks like:
{
"targets": [
{
"target_name": "wrapper",
"sources": ["src/wrapper.cpp"],
"libraries": [
"/home/hagen/Desktop/opencv-3.3.1/build/lib/libopencv_*.so"
],
'cflags_cc!': ['-fno-rtti', '-fno-exceptions'],
'cflags_cc+': ['-frtti', '-fexceptions']
}
]
}
I installed opencv like this: https://linuxhint.com/how-to-install-opencv-on-ubuntu/
The target, wrapper.cpp, looks roughly like:
#include <node.h>
#include <opencv2/opencv.hpp>
#include <opencv2/cvconfig.h>
using namespace cv;
void init(const v8::FunctionCallbackInfo<v8::Value>& args){
v8::Isolate* isolate = args.GetIsolate();
v8::String::Utf8Value param1(args[0]->ToString());
std::string video_path= "/home/hagen/Desktop/VisoProj-TBP/videos/street.mp4";
#if defined(HAVE_FFMPEG)
std::cout<<"ffmpeg yes\n";
#else
std::cout<<"no\n";
#endif
cv::VideoCapture vid=cv::VideoCapture(video_path, cv::CAP_FFMPEG);
if(!vid.isOpened())
{
std::cout << "!!! Failed to open file: " <<video_path << std::endl;
}
}
void Initialize(v8::Local<v8::Object> exports) {
NODE_SET_METHOD(exports, "init", init);
}
NODE_MODULE(wrapper, Initialize)
It tells me, that "HAVE_FFMPEG" ist true, but it does not open the .mp4 file. Other OpenCV functions, like imread() and imwrite do work fine. In a standalone .cpp application, I can use OpenCV with ffmpeg and I can read that specific .mp4 file with the same absolute path.
Any Ideas, how to get the VideoCapture working with the .mp4 file? Maybe my binding is wrong?
Thanks in advance!
So lately I've been getting into OpenCV with C++.
I've built up a few libraries and apps that I would like to export over to Nodejs, but I can't figure it out for the life of me.
I tried to check out how he did it in this repo below, but it was a lot to take in especially since this is my first add-on.
https://github.com/peterbraden/node-opencv/blob/master/binding.gyp
I don't mind it being with NAN or N-API, I just am hoping for something simple and easy to see what goes where and why.
Here is a simple OpenCV function that just opens up an image that I am trying to use as an addon with Node:
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
int ShowImage()
{
String imageName("./image.png");
Mat image;
image = imread(imageName, IMREAD_COLOR);
namedWindow("Display window", WINDOW_AUTOSIZE);
imshow("Display window", image);
waitKey(0);
}
There are three main files that you will need.
binding.gyp
module.cpp
index.js
binding.gyp
For me The hardest part was figuring out how to include openCV into the project. I don't know if this is correct or not but I looked at the binding.gyp file like a make file in a typical C++ project. With that in mind this is what my binding.gyp file looks like.
{
"targets": [{
"target_name": "module",
'include_dirs': [
'.',
'/user/local/lib',
],
'cflags': [
'-std=c++11',
],
'link_settings': {
'libraries': [
'-L/user/local/lib',
'-lopencv_core',
'-lopencv_imgproc',
'-lopencv_highgui'
],
},
"sources": [ "./src/module.cpp",
"./src/ImageProcessing.cpp" ]
}]
}
The ImageProcessing.cpp file that I wrote needed c++11 so that's why I added that flag it is not necessary to get openCV to work.
The key of the binding.gyp file is the link-settings. This is how you actually include openCV into your project.
Also make sure to include all of your source files in the sources list(I forgot to include my ImageProcessing.cpp file initially)
module.cpp
I used n-api so my module.cpp file looked like this
#include <node_api.h>
#include "ImageProcessing.hpp"
#include "opencv.hpp"
template <typename T>
ostream& operator<<(ostream& output, std::vector<T> const& values)
{
for (auto const& value : values)
{
output << value;
}
return output;
}
napi_value processImages(napi_env env, napi_callback_info info)
{
napi_status status;
size_t argc = 3;
napi_value argv[1];
status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
char PathName[100];
size_t result;
status = napi_get_value_string_utf8(env, argv[0], PathName, 100, &result);
char FileName1[100];
status = napi_get_value_string_utf8(env, argv[1], FileName1, 100, &result);
char FileName2[100];
status = napi_get_value_string_utf8(env, argv[2], FileName2, 100, &result);
vector< vector<Point> > Anchors; //to store coordinates of all anchor points
vector< vector<Point> > Regions[4]; //to store coordinates of all corners of all pages
vector<int> Parameters; // image processing parameters
vector<string> FileList1;
vector<string> FileList2;
Mat TemplateROI[NUM_SHEET][4];
Mat Result1, Result2;
string FileName;
string testName = FileName1;
int i;
// The first function to be called only at startup of the program
// provide the path to folder where the data and reference image files are saved
getAnchorRegionRoI(PathName, &Anchors, Regions, &Parameters, TemplateROI);
vector< vector<int> > Answers;
if (Parameters.at(0)) {
namedWindow("Display1", CV_WINDOW_AUTOSIZE);
namedWindow("Display2", CV_WINDOW_AUTOSIZE);
}
napi_value outer;
status = napi_create_array(env, &outer);
//This will need to be changed to watch for new files and then process them
Answers = scanBothSides(FileName1, FileName2, "./Output/", &Result1, &Result2, &Anchors, Regions, Parameters, TemplateROI);
for(int k = 0; k<Answers.size(); k++){
napi_value inner;
status = napi_create_array(env, &inner);
int j;
for(j = 0; j<Answers[k].size(); j++){
napi_value test;
napi_create_int32(env, Answers[k][j], &test);
napi_set_element(env,inner, j, test);
}
napi_value index;
napi_create_int32(env, k, &index);
napi_set_element(env,inner, j, index);
napi_set_element(env,outer, k, inner);
}
if (Parameters.at(0)) {
if (!Result1.empty() && !Result1.empty()) {
FileName = "./Output/" + string("O ") + FileList1[i];
imwrite(FileName, Result1);
FileName = "./Output/" + string("O ") + FileList2[i];
imwrite(FileName, Result2);
resize(Result1, Result1, Size(772, 1000));
resize(Result2, Result2, Size(772, 1000));
imshow("Display1", Result1);
imshow("Display2", Result2);
waitKey(0);
}
}
if (status != napi_ok)
{
napi_throw_error(env, NULL, "Failed to parse arguments");
}
//return PathName;
return outer;
}
napi_value Init(napi_env env, napi_value exports)
{
napi_status status;
napi_value fn;
status = napi_create_function(env, NULL, 0, processImages, NULL, &fn);
if (status != napi_ok)
{
napi_throw_error(env, NULL, "Unable to wrap native function");
}
status = napi_set_named_property(env, exports, "processImages", fn);
if (status != napi_ok)
{
napi_throw_error(env, NULL, "Unable to populate exports");
}
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
This is the file that interfaces with C/C++ and node.
I had trouble with the opencv.hpp file being found so I just moved it into my working directory for now. This is why I used quotes instead of brackets to include it.
Working with the n-api took a little getting used to so make sure you read the docs here
index.js
And finally here is my index.js file
const express = require('express');
const app = express();
const addon = require('./build/Release/module');
const value = "./Data/";
let FileName1 = "./Images/Back1.jpg";
let FileName2 = "./Images/Front1.jpg";
let result = addon.processImages(value, FileName1, FileName2);
console.log("Results: "+result);
server.listen(3000, () => console.log('Example app listening on port 3000!'))
So all you have to do is require your module from the build/Release folder and then call it like any other js function.
Take a look at the module.cpp code again and you will see that in the init function you use the n-api to create a new function. I called mine processImages. This name matches the name of the processImages function at the top of the module.cpp file. Finally in my index.js file I call addon.processImages().
Tips:
I installed node-gyp globally by running npm install -g node-gyp
I compiled my code using the following command: node-gyp configure build
Try getting a simple n-api project working first then add in openCV. I used this tutorial to get started
I created a simple script which will compile OpenCV 3.4 with opencv_contrib (SIFT, SURF available) for statically linking with Native Abstractions for Node.js.
rm -rf 3rdparty/opencv
mkdir -p 3rdparty/opencv
rm -rf tmp
mkdir tmp
cd tmp
rm -rf opencv-master
rm -rf opencv_contrib-master
git clone --branch 3.4 --depth 1 https://github.com/opencv/opencv.git opencv-master
git clone --branch 3.4 --depth 1 https://github.com/opencv/opencv_contrib.git opencv_contrib-master
mkdir build
cd build
cmake \
-DCMAKE_INSTALL_PREFIX="$(pwd)/../../3rdparty/opencv\#3.4" \
-DBUILD_SHARED_LIBS=OFF \
-DENABLE_PRECOMPILED_HEADERS=YES \
-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib-master/modules \
../opencv-master
cmake --build .
make install
It will help you to to get started with your computer vision project. By the way, SIFT algorithm is no longer requires a license for use as its patent expired 2020-03-06
{
"targets": [
{
"target_name": "addon",
"cflags": [
"-std=c++11",
"-stdlib=libc++"
],
"cflags_cc!": [
"-fno-rtti",
"-fno-exceptions"
],
"xcode_settings": {
"GCC_ENABLE_CPP_RTTI": "YES",
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
},
"include_dirs": [
"../../3rdparty/opencv/#3.4/include",
"<!(node -e \"require('nan')\")"
],
'libraries': [
"<!#(node utils/find-libs.js)",
"-framework OpenCL"
],
"sources": [
"./src/main.cc",
"./src/lib/MainAddon.cc"
],
}
]
}
The full source code is large enough so It published in this GitHub repository