持续在不同的项目中碰到 npm install、pnpm install 或者 yarn add 时的报错,虽然在某个项目中解决了,但在另外的项目中又会碰到,干脆记录下来。

1726404339550 49d78e5b e885 4619 be3e 7280efe2c553

错误

类似这样:

plain Exit code: 1 Command: node-gyp rebuild Arguments: Directory: /Users/jeff-tian/repos/weapp/node_modules/metrohash Output: gyp info it worked if it ends with ok gyp info using node-gyp@8.4.1 gyp info using node@18.17.0 | darwin | arm64 gyp info find Python using Python version 3.12.6 found at /opt/homebrew/opt/python@3.12/bin/python3.12 gyp info spawn /opt/homebrew/opt/python@3.12/bin/python3.12 gyp info spawn args [ gyp info spawn args /Users/jeff-tian/repos/weapp/node_modules/node-gyp/gyp/gyp_main.py, gyp info spawn args binding.gyp, gyp info spawn args -f, gyp info spawn args make, gyp info spawn args -I, gyp info spawn args /Users/jeff-tian/repos/weapp/node_modules/metrohash/build/config.gypi, gyp info spawn args -I, gyp info spawn args /Users/jeff-tian/repos/weapp/node_modules/node-gyp/addon.gypi, gyp info spawn args -I, gyp info spawn args /Users/jeff-tian/Library/Caches/node-gyp/18.17.0/include/node/common.gypi, gyp info spawn args -Dlibrary=shared_library, gyp info spawn args -Dvisibility=default, gyp info spawn args -Dnode_root_dir=/Users/jeff-tian/Library/Caches/node-gyp/18.17.0, gyp info spawn args -Dnode_gyp_dir=/Users/jeff-tian/repos/weapp/node_modules/node-gyp, gyp info spawn args -Dnode_lib_file=/Users/jeff-tian/Library/Caches/node-gyp/18.17.0/<(target_arch)/node.lib, gyp info spawn args -Dmodule_root_dir=/Users/jeff-tian/repos/weapp/node_modules/metrohash, gyp info spawn args -Dnode_engine=v8, gyp info spawn args --depth=., gyp info spawn args --no-parallel, gyp info spawn args --generator-output, gyp info spawn args build, gyp info spawn args -Goutput_dir=. gyp info spawn args ] Traceback (most recent call last): File /Users/jeff-tian/repos/weapp/node_modules/node-gyp/gyp/gyp_main.py, line 42, in import gyp # noqa: E402 ^^^^^^^^^^ File /Users/jeff-tian/repos/weapp/node_modules/node-gyp/gyp/pylib/gyp/init.py, line 9, in import gyp.input File /Users/jeff-tian/repos/weapp/node_modules/node-gyp/gyp/pylib/gyp/input.py, line 19, in from distutils.version import StrictVersion ModuleNotFoundError: No module named distutils gyp ERR! configure error gyp ERR! stack Error: gyp failed with exit code: 1 gyp ERR! stack at ChildProcess.onCpExit (/Users/jeff-tian/repos/weapp/node_modules/node-gyp/lib/configure.js:259:16) gyp ERR! stack at ChildProcess.emit (node:events:514:28) gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:291:12) gyp ERR! System Darwin 23.3.0 gyp ERR! command /Users/jeff-tian/.nvm/versions/node/v18.17.0/bin/node /Users/jeff-tian/repos/weapp/node_modules/metrohash/node_modules/.bin/node-gyp rebuild gyp ERR! cwd /Users/jeff-tian/repos/weapp/node_modules/metrohash

或者:

plain .../node_modules/node-sass postinstall$ node scripts/build.js │ Binary found at /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/node-sa… │ Testing binary │ Binary has a problem: Error: dlopen(/Users/jeff-tian/brickverse/cool-project/node_m… │ at Module._extensions..node (node:internal/modules/cjs/loader:1340:18) │ at Module.load (node:internal/modules/cjs/loader:1119:32) │ at Module._load (node:internal/modules/cjs/loader:960:12) │ at Module.require (node:internal/modules/cjs/loader:1143:19) │ at require (node:internal/modules/cjs/helpers:110:18) │ at module.exports (/Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/… │ at Object. (/Users/jeff-tian/brickverse/cool-project/node_modules/.p… │ at Module._compile (node:internal/modules/cjs/loader:1256:14) │ at Module._extensions..js (node:internal/modules/cjs/loader:1310:10) │ at Module.load (node:internal/modules/cjs/loader:1119:32) { │ code: ERR_DLOPEN_FAILED │ } │ Building the binary locally │ Building: /Users/jeff-tian/.nvm/versions/node/v18.17.0/bin/node /Users/jeff-tian/brickverse/cce-… │ gyp info it worked if it ends with ok │ gyp verb cli [ │ gyp verb cli /Users/jeff-tian/.nvm/versions/node/v18.17.0/bin/node, │ gyp verb cli /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/node-gy… │ gyp verb cli rebuild, │ gyp verb cli --verbose, │ gyp verb cli --libsass_ext=, │ gyp verb cli --libsass_cflags=, │ gyp verb cli --libsass_ldflags=, │ gyp verb cli --libsass_library= │ gyp verb cli ] │ gyp info using node-gyp@8.4.1 │ gyp info using node@18.17.0 | darwin | arm64 │ gyp verb command rebuild [] │ gyp verb command clean [] │ gyp verb clean removing build directory │ gyp verb command configure [] │ gyp verb find Python Python is not set from command line or npm configuration │ gyp verb find Python Python is not set from environment variable PYTHON │ gyp verb find Python checking if python3 can be used │ gyp verb find Python - executing python3 to get executable path │ gyp verb find Python - executable path is /opt/homebrew/opt/python@3.12/bin/python3.12 │ gyp verb find Python - executing /opt/homebrew/opt/python@3.12/bin/python3.12 to get v… │ gyp verb find Python - version is 3.12.6 │ gyp info find Python using Python version 3.12.6 found at /opt/homebrew/opt/python@3.12… │ gyp verb get node dir no --target version specified, falling back to host node version: … │ gyp verb command install [ 18.17.0 ] │ gyp verb install input version string 18.17.0 │ gyp verb install installing version: 18.17.0 │ gyp verb install --ensure was passed, so wont reinstall if already installed │ gyp verb install version is already installed, need to check installVersion │ gyp verb got installVersion 9 │ gyp verb needs installVersion 9 │ gyp verb install version is good │ gyp verb get node dir target node version installed: 18.17.0 │ gyp verb build dir attempting to create build dir: /Users/jeff-tian/brickverse/cce-short-link… │ gyp verb build dir build dir needed to be created? Yes │ gyp verb build/config.gypi creating config file │ gyp verb build/config.gypi writing out config file: /Users/jeff-tian/brickverse/cce-short-link-… │ gyp verb config.gypi checking for gypi file: /Users/jeff-tian/brickverse/cce-short-link-dashboa… │ gyp verb common.gypi checking for gypi file: /Users/jeff-tian/brickverse/cce-short-link-dashboa… │ gyp verb gyp gyp format was not specified; forcing make │ gyp info spawn /opt/homebrew/opt/python@3.12/bin/python3.12 │ gyp info spawn args [ │ gyp info spawn args /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/… │ gyp info spawn args binding.gyp, │ gyp info spawn args -f, │ gyp info spawn args make, │ gyp info spawn args -I, │ gyp info spawn args /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/… │ gyp info spawn args -I, │ gyp info spawn args /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/… │ gyp info spawn args -I, │ gyp info spawn args /Users/jeff-tian/Library/Caches/node-gyp/18.17.0/include/node/comm… │ gyp info spawn args -Dlibrary=shared_library, │ gyp info spawn args -Dvisibility=default, │ gyp info spawn args -Dnode_root_dir=/Users/jeff-tian/Library/Caches/node-gyp/18.17.0, │ gyp info spawn args -Dnode_gyp_dir=/Users/jeff-tian/brickverse/cool-project/node… │ gyp info spawn args -Dnode_lib_file=/Users/jeff-tian/Library/Caches/node-gyp/18.17.0/<… │ gyp info spawn args -Dmodule_root_dir=/Users/jeff-tian/brickverse/cool-project/n… │ gyp info spawn args -Dnode_engine=v8, │ gyp info spawn args --depth=., │ gyp info spawn args --no-parallel, │ gyp info spawn args --generator-output, │ gyp info spawn args build, │ gyp info spawn args -Goutput_dir=. │ gyp info spawn args ] │ Traceback (most recent call last): │ File /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/node-gyp@https+… │ import gyp # noqa: E402 │ ^^^^^^^^^^ │ File /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/node-gyp@https+… │ import gyp.input │ File /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/node-gyp@https+… │ from distutils.version import StrictVersion │ ModuleNotFoundError: No module named distutils │ gyp ERR! configure error │ gyp ERR! stack Error: gyp failed with exit code: 1 │ gyp ERR! stack at ChildProcess.onCpExit (/Users/jeff-tian/brickverse/cce-short-link-dashboa… │ gyp ERR! stack at ChildProcess.emit (node:events:514:28) │ gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:291:12) │ gyp ERR! System Darwin 23.3.0 │ gyp ERR! command /Users/jeff-tian/.nvm/versions/node/v18.17.0/bin/node /Users/jeff-tian… │ gyp ERR! cwd /Users/jeff-tian/brickverse/cool-project/node_modules/.pnpm/node-sass@… │ gyp ERR! node -v v18.17.0 │ gyp ERR! node-gyp -v v8.4.1 │ gyp ERR! not ok │ Build failed with error code: 1

看上去都是和 gyp 有关,并且都提到一个 distutils。我注意到很多项目中都会直接或者间接地引用 node-sass,一旦对它有直接或者间接的依赖,就很容易出现这个问题。

这个 distutils 似乎是一个 python 包,可以通过 python3 -c import distuils进行测试,如果这个命令报错,那就会出现上述错误。

解决办法

一般可以通过 python 的虚拟环境解决。

创建并激活虚拟环境

这可以隔离 python 包,以避免和系统级别的包产生冲突。

plain python3 -m venv venv source venv/bin/activate

安装 setuptools 和 wheel

这些包在构建和安装其他包时常常都是需要的。

plain python3 -m pip install --upgrade pip python3 -m pip install setuptools wheel

验证 distutils 安装成功了

确保 distutils 在虚拟环境中是可用的。

plain python3 -c import distutils

重新安装依赖

这时重新执行 npm install 、 pnpm install 或者 yarn add 就可以成功了。