如何在NodeJS自定义二进制文件中包含其他模块



我正在为嵌入式系统从最新的代码库构建NodeJS的自定义二进制文件。我有几个模块,我想作为二进制文件的标准提供,或者甚至运行一个自定义脚本,该脚本被编译成二进制文件,可以通过命令行选项调用。

所以有两个问题:

1) 我隐约记得那个节点在构建时允许包含自定义模块,但我浏览了最新的5.9.0配置脚本,看不到任何相关的内容,或者可能我错过了它。

2) 有人已经做了类似的事情吗?如果是,您提出的最佳实践是什么?

我不是在寻找像Electron或其他二进制捆绑器这样的东西,而是在节点二进制中构建。

谢谢,

Andy

所以我想我比想象的要快得多。

对于其他人,您可以向其中添加任何NPM模块,只需将实际的源文件添加到node.gyp配置文件中。

编译它并运行自定义二进制文件。现在都在里面了。

> var cmu = require("cmu");
undefined
> cmu
{ version: [Function] }
> cmu.version()
'It worked!'
> `

在研究了很长一段时间之后,我不得不说flyandi的答案不太正确。您不能仅通过将任何NPM模块添加到node.gyp.来添加

您只能通过这种方式添加纯JavaScript模块。为了能够嵌入C++模块(我故意不使用"native"这个词,因为这个词在nodeJS术语中非常模糊——只需查看源代码即可)。

总结如下:

要将JS模块嵌入到自定义nodejs中,只需将其添加到node.gyp文件的library_files部分即可。还要注意,它应该放在lib文件夹中,否则在需要该模块时会遇到问题。这是因为node.gyp/library_files中列出的名称/路径用于对node_javascript.cc中间文件中模块的id进行编码,然后在搜索内置模块时使用该中间文件。

嵌入本机模块要困难得多。到目前为止,我发现的最好的方法是将模块构建为静态库,而不是动态库,对于基于cmake(-js)的模块,可以通过将SHARED参数更改为static来实现,如下所示:

add_library(${PROJECT_NAME} STATIC ${SRC})

而不是:

add_library(${PROJECT_NAME} SHARED ${SRC})

同时更改后缀:

set_target_properties(
  ${PROJECT_NAME}
  PROPERTIES
  PREFIX "" 
  SUFFIX ".lib") /* instead of .node */

然后,您可以通过添加以下部分从node.gyp链接它:

'link_settings': {
    'libraries' : [ 
      "path/to/my/library.lib",
      #...add other static dependencies
    ],
},

(如何用基于节点gyp的项目做到这一点应该很容易谷歌)

这允许您构建模块,但您将无法要求它,因为node中的require()函数只能用于加载内置JS模块、外部JS模块或外部动态节点模块。但现在我们有了一个内置的C++模块。很多节点集成模块都是C++,但它们在/lib中总是有一个JS包装器,而那些包装器则使用process.binding()来加载C++模块。也就是说,process.binding()是一种用于集成C++模块的require()函数。

也就是说,我们还需要调用require.binding(),而不是要求加载我们的集成模块。要做到这一点,我们必须首先使我们的模块"内置"。

我们可以通过更换来做到这一点

NODE_MODULE(mymodule, InitAll)

使用输入模块定义

NODE_BUILTIN_MODULE_CONTEXT_AWARE(mymodule, InitAll)

它将把它注册为内部模块,从现在起我们可以process.binding()

请注意,NODE_BUILTIN_MODULE_CONTEXT_AWAREnode.h中没有定义为NODE_MODULE,而是在node_internals.h中,因此您必须包含该定义,或者将宏定义复制到cpp文件中(第一个定义当然更好,因为nodejs API往往经常更改…)

我们需要做的最后一件事是将我们新集成的模块列在其他模块中,以便节点知道初始化它们(也就是说,将它们包括在搜索加载了process.binding()的模块时使用的模块列表中)。在node_internals.h中有这样一个宏:

#define NODE_BUILTIN_STANDARD_MODULES(V)                                      
    V(async_wrap)                                                             
    V(buffer)                                                                 
    V(cares_wrap)                                                             
...

所以,只需像其他V(mymodule)一样,将您的模块添加到列表中即可。

我可能忘记了一些步骤,所以请在评论中询问你是否认为我错过了什么。

如果你想知道为什么会有人想这么做。。。您可以提出几个原因,但这里有一个对我来说最重要的原因:那些用于将项目打包在一个可执行文件中的包管理器(如pkg或nexe)只能使用基于节点gyp的模块。如果你和我一样,需要使用基于cmake的模块,那么最终的可执行文件将无法工作。。。

最新更新