node-gyp: run binding.gyp in all subdirectories - node.js

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)

Related

Make Yarn don't hoist the dependencies of the specific package

In below project, I want all dependencies of TodoList will no be hosted to node_modules in root directory of monorepo.
Below settings is not enough to reach this effect:
{
"private": true,
"workspaces": {
"packages": [ "BusinessRules", "Server", "TodoList" ],
"nohoist": [
"TodoList/**"
]
}
}
"TodoList/**/**" is not enough too.
How to make all dependencies of TodoList will be inside TodoList/node_modules?
Found the solution here.
In this case, it's required to add below JSON in TodoList/package.json:
"workspaces": {
"nohoist": ["**"]
},

Vue/Node error : Rule can only have one resource source

I am brand new to Vue and Node and everything was going well with a Vue3 project I was messing around with to learn. I wanted to use scss files so installed sass-loader via npm using:
npm install sass-loader sass webpack --save-dev
And since then the app is broken, now when I try to serve I get the following error:
Error: Rule can only have one resource source (provided resource and test + include + exclude) in {
"exclude": [
null
],
"use": [
{
"loader": "C:\\pathtoapp\\node_modules\\cache-loader\\dist\\cjs.js",
"options": {
"cacheDirectory": "C:\\pathtoapp\\node_modules\\.cache\\babel-loader",
"cacheIdentifier": "43be597c"
},
"ident": "clonedRuleSet-38.use[0]"
},
{
"loader": "C:\\pathtoapp\\node_modules\\babel-loader\\lib\\index.js",
"options": "undefined",
"ident": "undefined"
}
]
}
Error: Rule can only have one resource source (provided resource and test + include + exclude) in {
"exclude": [
null
],
"use": [
{
"loader": "C:\\pathtoapp\\node_modules\\cache-loader\\dist\\cjs.js",
"options": {
"cacheDirectory": "C:\\pathtoapp\\node_modules\\.cache\\babel-loader",
"cacheIdentifier": "43be597c"
},
"ident": "clonedRuleSet-38.use[0]"
},
{
"loader": "C:\\pathtoapp\\node_modules\\babel-loader\\lib\\index.js",
"options": "undefined",
"ident": "undefined"
}
]
}
I looked up this error and most believed this to be an issue with webpack but I have uninstalled and installed again. Installed an earlier version of webpack, tried changing package.json to point to an earlier version, tried anything I can currently find on SO and now I'm completely stumped.
Any assistance on this would be much appreciated as I'd rather learn and discover how to fix the problem should I encounter it again rather than simply start a new project. Let me know any code/files I should post in an edit where required.
I ran into the same issue and I was able to resolve it by:
rm -rf node_modules
rm package-lock.json
npm install --legacy-peer-deps
Source

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.

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.

Resources