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

时间:2016-03-23 05:38:40

标签: node.js

我正在从嵌入式系统的最新代码库构建NodeJS的自定义二进制文件。我有几个模块,我希望以二进制文件的形式发布 - 或者甚至运行自定义脚本,它被编译成二进制文件,可以通过命令行选项调用。

所以有两个问题:

1)我依稀记得节点允许在构建期间包含自定义模块,但我查看了最新的5.9.0配置脚本,我看不到任何相关内容 - 或者我可能错过了它。

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

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

谢谢,

安迪

2 个答案:

答案 0 :(得分:0)

所以我想我想出来的速度要快得多。

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

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

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

答案 1 :(得分:0)

经过一段时间的研究,我不得不说flyandi的答案并不完全正确。您只需将任何 NPM模块添加到node.gyp即可添加任何 NPM模块。

您只能以这种方式添加纯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
    ],
},

(对于基于node-gyp的项目,如何做到这一点应该很容易谷歌)

这允许您构建模块,但您将无法满足它,因为节点中的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_AWARE中的node.h未定义为NODE_MODULE,而是node_internals.h,因此您必须包含该{1}},或将宏定义复制到您的cpp文件(第一个当然更好,因为nodejs API往往会经常更改......)。

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

process.binding()

所以只需将您的模块添加到列表中的方式与其他#define NODE_BUILTIN_STANDARD_MODULES(V) \ V(async_wrap) \ V(buffer) \ V(cares_wrap) \ ... 相同。

我可能已经忘记了一些步骤,所以如果你认为我错过了什么,请在评论中提问。

如果你想知道为什么有人甚至想要这样做......你可以提出几个原因,但这对我来说是最重要的:那些包管理器用于将你的项目包装在一个可执行文件中(如pkg或nexe)仅适用于基于node-gyp的模块。如果您和我一样需要使用基于cmake的模块,那么最终的可执行文件将不起作用......