【Nodejs】关于原生模块编译node-gyp + prebuild-install (以安装better-sqlite3为例)

本文围绕Node.js相关工具展开,介绍了node-gyp这一跨平台命令行工具,用于编译Node.js原生插件模块。还讲述了prebuild-install会优先下载预编译二进制文件。重点追踪了better-sqlite3的安装过程,包括其package命令脚本等,最后提及让prebuild-install通过淘宝源下载及Node.js 16版本的配置问题。

关于node-gyp

node-gyp是一个用 Node.js 编写的跨平台命令行工具,用于为 Node.js 编译本机插件模块。它包含之前由 Chromium 团队使用的 gyp-next项目的供应副本,扩展以支持 Node.js 原生插件的开发。

node-gyp is a cross-platform command-line tool written in Node.js for compiling native addon modules for Node.js. It contains a vendored copy of the gyp-next project that was previously used by the Chromium team, extended to support the development of Node.js native addons.

node是跨平台的,那么对于任何的node模块理论也是应该是跨平台的。然而,有些node模块直接或间接使用原生C/C++代码,这些东西要跨平台,就需要使用源码根据实际的操作平台环境进行原生模块编译。通常我们开发环境为macOS或Windows,而生产环境为Linux的各种发行版,这将导致我们的开发工作变得沉重不堪。那我们是否可以跳过node-gyp的编译过程?

prebuild-install

node-gyp的编译是让人难受的过程,所以社区出现了node-pre-gypprebuild-install,它们都会优先下载插件作者预编译的二进制文件,当二进制文件下载出现问题时,再使用node-gyp进行编译兜底。但因为我们网络环境的特殊性,这些二进制文件我们大概率是不会下载成功的,接下来一起来看看在better-sqlite3的安装过程中prebuild-install干了什么事。

关于node-pre-gyp参考姊妹文【Nodejs】关于原生模块编译node-gyp + node-pre-gyp (以安装canvas为例)

better-sqlite3安装过程追踪

better-sqlite3就使用了prebuild-install来优化构建过程

1. 安装better-sqlite3

关于install我们需要了解一点东西, 通常基于表象我们都会认为npm下载解压并释放到node_modules后就完成了安装过程,但实际上npm还会检查package中是否配置了install命令,存在就会立即执行。

npm install better-sqlite3
2. better-sqlite3的package的命令脚本

可以看到better-sqlite3配置了install命令,所以npm下载better-sqlite3后立即执行了prebuild-install

{
    ...,
    "scripts": {
        "install": "prebuild-install || npm run build-release",
        "build-release": "node-gyp rebuild --release",
        "build-debug": "node-gyp rebuild --debug",
        "rebuild-release": "npm run lzz && npm run build-release",
        "rebuild-debug": "npm run lzz && npm run build-debug",
        "test": "mocha --exit --slow=75 --timeout=5000",
        "benchmark": "node benchmark",
        "download": "bash ./deps/download.sh",
        "lzz": "lzz -hx hpp -sx cpp -k BETTER_SQLITE3 -d -hl -sl -e ./src/better_sqlite3.lzz"
      },
}
3. prebuild-install的package.json

可以看到prebuild-install使用bin将prebuild-install命令链接到了bin.js

{
    ...,
    "bin": "./bin.js",
    ...
}
4. 下载预构建的二进制文件

可以看到bin.js中核心函数就是startDownload

const startDownload = function (downloadUrl) {
  download(downloadUrl, opts, function (err) {
    if (err) {
      log.warn('install', err.message)
      return process.exit(1)
    }
    log.info('install', 'Successfully installed prebuilt binary!')
  })
}
if (opts.token) {
  asset(opts, function (err, assetId) {
    if (err) {
      log.warn('install', err.message)
      return process.exit(1)
    }
    startDownload(util.getAssetUrl(opts, assetId))
  })
} else {
  startDownload(util.getDownloadUrl(opts))
}

其中opts的核心参数项如下。粘贴的代码有删减,完整的请自行查阅源码

const rc = require('rc')('prebuild-install', {
    target: pkgConf.target || env.npm_config_target || process.versions.node,
    runtime: pkgConf.runtime || env.npm_config_runtime || 'node',
    arch: pkgConf.arch || env.npm_config_arch || process.arch,
    libc: libc,
    platform: env.npm_config_platform || process.platform,
    debug: env.npm_config_debug === 'true',
    force: false,
    verbose: env.npm_config_verbose === 'true',
    buildFromSource: buildFromSource === pkg.name || buildFromSource === 'true',
    path: '.',
    proxy: env.npm_config_proxy || env.http_proxy || env.HTTP_PROXY,
    'https-proxy': env.npm_config_https_proxy || env.https_proxy || env.HTTPS_PROXY,
    'local-address': env.npm_config_local_address,
    'local-prebuilds': 'prebuilds',
    'tag-prefix': 'v',
    download: env.npm_config_download
})

opts中并没有发现token的踪迹,那程序将进入else分支,接下来我们进入getDownloadUrl查看于构建二进制文件下载地址的拼接逻辑。可以看到针对包名prebuild-install会移除包名以@组织开头的部分

function getDownloadUrl (opts) {
  const pkgName = opts.pkg.name.replace(/^@[a-zA-Z0-9_\-.~]+\//, '')
  return expandTemplate(urlTemplate(opts), {
    name: pkgName,
    package_name: pkgName,
    version: opts.pkg.version,
    major: opts.pkg.version.split('.')[0],
    minor: opts.pkg.version.split('.')[1],
    patch: opts.pkg.version.split('.')[2],
    prerelease: opts.pkg.version.split('-')[1],
    build: opts.pkg.version.split('+')[1],
    abi: opts.abi || process.versions.modules,
    node_abi: process.versions.modules,
    runtime: opts.runtime || 'node',
    platform: opts.platform,
    arch: opts.arch,
    libc: opts.libc || '',
    configuration: (opts.debug ? 'Debug' : 'Release'),
    module_name: opts.pkg.binary && opts.pkg.binary.module_name,
    tag_prefix: opts['tag-prefix']
  })
}

接下来我们进入urlTemplate函数,函数内又调用了其他的函数,这里我们针对重要的代码做说明不粘贴源码

// 构建了二进制文件名的模板字符串, 在稍后`{}`中的内容将会被替换掉
const packageName = '{name}-v{version}-{runtime}-v{abi}-{platform}{libc}-{arch}.tar.gz'
// 获取npmrc中的镜像配置{包名}_binary_host(或{包名}_binary_host_mirror)
const hostMirrorUrl = getHostMirrorUrl(opts);
// 当hostMirrorUrl存在时, 在hostMirrorUrl拼接上版本号和包信息返回
if (hostMirrorUrl) {
    return hostMirrorUrl + '/{tag_prefix}{version}/' + packageName
}
// 当hostMirrorUrl不存在时, 将会检查包中是否配置了binary以及binary.host, 存在时返回格式为
// '{binary.host}/{binary.remote_path}/{binary.package_name || packageName}'
// 当binary以及binary.host均不存在时, 将会返回二进制文件在github上的托管地址
return github(opts.pkg) + '/releases/download/{tag_prefix}{version}/' + packageName

接下来就是expandTemplate函数对urlTemplate返回的下载地址中的占位符做替换处理生成真正的预构建二进制文件地址,至此prebuild-install完成了二进制文件的下载,跳过了node-pyg的编译过程

让prebuild-install通过淘宝源下载预构建文件

npm提供了.npmrc配置文件并注入到进程的env环境变量中,从上面的urlTemplate源码可知,prebuild-install会优先读取npm_config_{包名}_binary_host不存在时继续读取npm_config_{包名}_binary_host_mirror(.npmrc中的变量均会被npm添加npm_config_前缀, 所以我们配置时无需添加npm_config_前缀),另外需要值得注意的是npm会将.npmrc中的键以下划线的方式组织且任何非数字和字母的字符将会被替换为_。所以以better-sqlite3举例来说,以下配置均可被prebuild-install正常读取。

better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
better-sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
better-sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3

鉴于node-pre-gyp只读取{包名}_binary_host_mirror,针对原生模块我们在.npmrc中以{包名}_binary_host_mirror={mirror}的格式配置预构建文件下载镜像

关于Node.js 16版本

如果你使用Node.js 14版本,以上的配置是有效的,如果你使用的是Node.js 16版本,那我们配置的预构建文件地址不能按我们的预期工作。

我尚不清楚其中缘由,但better-sqlite3的作者指出需要在.npmrc中设置NODE_MODULE_VERSION=83,他没有说明为什么要这么做(原文)。

使用rebuild编译之后仍然报错,这是为什么,怎么修改? npm rebuild better-sqlite3 --build-from-source --runtime=electron --target=19.1.9 npm ERR! code 1 npm ERR! path D:\TPlearning\vct\vms\node_modules\better-sqlite3 npm ERR! command failed npm ERR! command C:\windows\system32\cmd.exe /d /s /c prebuild-install || node-gyp rebuild --release npm ERR! npm ERR! gyp info it worked if it ends with ok npm ERR! gyp info using node-gyp@10.3.1 npm ERR! gyp info using node@16.14.0 | win32 | x64 npm ERR! gyp info find Python using Python version 3.9.0 found at "C:\Users\admin\AppData\Local\Programs\Python\Python39-32\python.exe" npm ERR! gyp http GET https://nodejs.org/dist/v19.1.9/node-v19.1.9-headers.tar.gz npm ERR! gyp http 404 https://nodejs.org/dist/v19.1.9/node-v19.1.9-headers.tar.gz npm ERR! gyp WARN install got an error, rolling back install npm ERR! gyp ERR! configure error npm ERR! gyp ERR! stack Error: 404 response downloading https://nodejs.org/dist/v19.1.9/node-v19.1.9-headers.tar.gz npm ERR! gyp ERR! stack at go (D:\TPlearning\vct\vms\node_modules\node-gyp\lib\install.js:223:21) npm ERR! gyp ERR! stack at processTicksAndRejections (node:internal/process/task_queues:96:5) npm ERR! gyp ERR! stack at async install (D:\TPlearning\vct\vms\node_modules\node-gyp\lib\install.js:63:18) npm ERR! gyp ERR! stack at async getNodeDir (D:\TPlearning\vct\vms\node_modules\node-gyp\lib\configure.js:79:7) npm ERR! gyp ERR! stack at async run (D:\TPlearning\vct\vms\node_modules\node-gyp\bin\node-gyp.js:81:18) npm ERR! gyp ERR! System Windows_NT 10.0.26100 npm ERR! gyp ERR! command "D:\\Installed\\nodejs\\node.exe" "D:\\TPlearning\\vct\\vms\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild" "--release" npm ERR! gyp ERR! cwd D:\TPlearning\vct\vms\node_modules\better-sqlite3 npm ERR! gyp ERR! node -v v16.14.0 npm ERR! gyp ERR! node-gyp -v v10.3.1 npm ERR! gyp ERR! not ok npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\admin\AppData\Local\npm-cache\_logs\2025-09-22T02_16_44_971Z-debug-0.log
09-23
npm i sqlite3@5.1.7 npm WARN deprecated @npmcli/move-file@1.1.2: This functionality has been moved to @npmcli/fs npm WARN deprecated npmlog@6.0.2: This package is no longer supported. npm WARN deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported npm WARN deprecated are-we-there-yet@3.0.1: This package is no longer supported. npm WARN deprecated gauge@4.0.4: This package is no longer supported. npm ERR! code 1 npm ERR! path D:\node_control\node_modules\sqlite3 npm ERR! command failed npm ERR! command C:\Windows\system32\cmd.exe /d //s /c prebuild-install -r napi || node-gyp rebuild npm ERR! prebuild-install warn install connect ETIMEDOUT 20.205.243.166:443 npm ERR! gyp info it worked if it ends with ok npm ERR! gyp info using node-gyp@8.4.1 npm ERR! gyp info using node@18.14.2 | win32 | x64 npm ERR! gyp info find Python using Python version 3.8.5 found at &#39;D:\setup\python3.8.5\python.exe&#39; npm ERR! gyp http GET https://nodejs.org/download/release/v18.14.2/node-v18.14.2-headers.tar.gz npm ERR! gyp WARN install got an error, rolling back install npm ERR! gyp ERR! configure error npm ERR! gyp ERR! stack FetchError: request to https://nodejs.org/download/release/v18.14.2/node-v18.14.2-headers.tar.gz failed, reason: connect ETI MEDOUT 104.20.1.252:443 npm ERR! gyp ERR! stack at ClientRequest.<anonymous> (D:\node_control\node_modules\minipass-fetch\lib\index.js:110:14) npm ERR! gyp ERR! stack at ClientRequest.emit (node:events:513:28) npm ERR! gyp ERR! stack at TLSSocket.socketErrorListener (node:_http_client:502:9) npm ERR! gyp ERR! stack at TLSSocket.emit (node:events:525:35) npm ERR! gyp ERR! stack at emitErrorNT (node:internal//streams/destroy:151:8) npm ERR! gyp ERR! stack at emitErrorCloseNT (node:internal//streams/destroy:116:3) npm ERR! gyp ERR! stack at process.processTicksAndRejections (node:internal/process/task_queues:82:21) npm ERR! gyp ERR! System Windows_NT 10.0.17763 npm ERR! gyp ERR! command &#39;D:\\software\\nvm\\nodejs\\node.exe&#39; &#39;D:\\node_control\\node_modules\\node-gyp\\bin\\node-gyp.js&#39; &#39;rebuild&#39; npm ERR! gyp ERR! cwd D:\node_control\node_modules\sqlite3 npm ERR! gyp ERR! node -v v18.14.2 npm ERR! gyp ERR! node-gyp -v v8.4.1 npm ERR! gyp ERR! not ok npm ERR! A complete log of this run can be found in: npm ERR!
最新发布
01-09
visual studio tools not foundnpm ERR! code 1 npm ERR! path D:\Web Development\SpacebookLite-master\node_modules\sqlite3 npm ERR! command failed npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c prebuild-install -r napi || node-gyp rebuild npm ERR! prebuild-install warn install connect ECONNREFUSED 127.0.0.1:443 npm ERR! gyp info it worked if it ends with ok npm ERR! gyp info using node-gyp@8.4.1 npm ERR! gyp info using node@18.19.0 | win32 | x64 npm ERR! gyp info find Python using Python version 3.13.7 found at "D:\python\python.exe" npm ERR! gyp ERR! find VS npm ERR! gyp ERR! find VS msvs_version not set from command line or npm config npm ERR! gyp ERR! find VS VCINSTALLDIR not set, not running in VS Command Prompt npm ERR! gyp ERR! find VS could not use PowerShell to find Visual Studio 2017 or newer, try re-running with &#39;--loglevel silly&#39; for more details npm ERR! gyp ERR! find VS looking for Visual Studio 2015 npm ERR! gyp ERR! find VS - not found npm ERR! gyp ERR! find VS not looking for VS2013 as it is only supported up to Node.js 8 npm ERR! gyp ERR! find VS npm ERR! gyp ERR! find VS ************************************************************** npm ERR! gyp ERR! find VS You need to install the latest version of Visual Studio npm ERR! gyp ERR! find VS including the "Desktop development with C++" workload. npm ERR! gyp ERR! find VS For more information consult the documentation at: npm ERR! gyp ERR! find VS https://github.com/nodejs/node-gyp#on-windows npm ERR! gyp ERR! find VS ************************************************************** npm ERR! gyp ERR! find VS npm ERR! gyp ERR! configure error npm ERR! gyp ERR! stack Error: Could not find any Visual Studio installation to use npm ERR! gyp ERR! stack at VisualStudioFinder.fail (D:\Web Development\SpacebookLite-master\node_modules\node-gyp\lib\find-visualstudio.js:122:47) npm ERR! gyp ERR! stack at D:\Web Development\SpacebookLite-master\node_modules\node-gyp\lib\find-visualstudio.js:75:16 npm ERR! gyp ERR! stack at VisualStudioFinder.findVisualStudio2013 (D:\Web Development\SpacebookLite-master\node_modules\node-gyp\lib\find-visualstudio.js:363:14) npm ERR! gyp ERR! stack at D:\Web Development\SpacebookLite-master\node_modules\node-gyp\lib\find-visualstudio.js:71:14 npm ERR! gyp ERR! stack at D:\Web Development\SpacebookLite-master\node_modules\node-gyp\lib\find-visualstudio.js:384:16 npm ERR! gyp ERR! stack at D:\Web Development\SpacebookLite-master\node_modules\node-gyp\lib\util.js:54:7 npm ERR! gyp ERR! stack at D:\Web Development\SpacebookLite-master\node_modules\node-gyp\lib\util.js:33:16 npm ERR! gyp ERR! stack at ChildProcess.exithandler (node:child_process:430:5) npm ERR! gyp ERR! stack at ChildProcess.emit (node:events:517:28) npm ERR! gyp ERR! stack at maybeClose (node:internal/child_process:1098:16) npm ERR! gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:303:5) npm ERR! gyp ERR! System Windows_NT 10.0.26100 npm ERR! gyp ERR! command "D:\\Web Development\\nodejs\\node.exe" "D:\\Web Development\\SpacebookLite-master\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild" npm ERR! gyp ERR! cwd D:\Web Development\SpacebookLite-master\node_modules\sqlite3 npm ERR! gyp ERR! node -v v18.19.0 npm ERR! gyp ERR! node-gyp -v v8.4.1 npm ERR! gyp ERR! not ok npm ERR! A complete log of this run can be found in: C:\Users\Asus\AppData\Local\npm-cache\_logs\2025-09-10T11_27_10_802Z-debug-0.log
09-11
npm ERR! code 1 npm ERR! path D:\node.js\node_global\node_modules\n8n\node_modules\sqlite3 npm ERR! command failed npm ERR! command C:\Windows\system32\cmd.exe /d /s /c prebuild-install -r napi || node-gyp rebuild npm ERR! prebuild-install warn install connect ETIMEDOUT 20.205.243.166:443 npm ERR! gyp info it worked if it ends with ok npm ERR! gyp info using node-gyp@8.4.1 npm ERR! gyp info using node@18.16.0 | win32 | x64 npm ERR! gyp info find Python using Python version 3.11.3 found at "C:\Python311\python.exe" npm ERR! gyp ERR! find VS npm ERR! gyp ERR! find VS msvs_version not set from command line or npm config npm ERR! gyp ERR! find VS VCINSTALLDIR not set, not running in VS Command Prompt npm ERR! gyp ERR! find VS checking VS2022 (17.6.33723.286) found at: npm ERR! gyp ERR! find VS "C:\Program Files\Microsoft Visual Studio\2022\Community" npm ERR! gyp ERR! find VS - found "Visual Studio C++ core features" npm ERR! gyp ERR! find VS - found VC++ toolset: v143 npm ERR! gyp ERR! find VS - missing any Windows SDK npm ERR! gyp ERR! find VS could not find a version of Visual Studio 2017 or newer to use npm ERR! gyp ERR! find VS looking for Visual Studio 2015 npm ERR! gyp ERR! find VS - not found npm ERR! gyp ERR! find VS not looking for VS2013 as it is only supported up to Node.js 8 npm ERR! gyp ERR! find VS npm ERR! gyp ERR! find VS ************************************************************** npm ERR! gyp ERR! find VS You need to install the latest version of Visual Studio npm ERR! gyp ERR! find VS including the "Desktop development with C++" workload. npm ERR! gyp ERR! find VS For more information consult the documentation at: npm ERR! gyp ERR! find VS https://github.com/nodejs/node-gyp#on-windows npm ERR! gyp ERR! find VS ************************************************************** npm ERR! gyp ERR! find VS npm ERR! gyp ERR! configure error npm ERR! gyp ERR! stack Error: Could not find any Visual Studio installation to use npm ERR! gyp ERR! stack at Visu
08-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值