gem 包安装失败,竟然是 Nix 惹的祸,害我几乎把谷歌找到的帖子都看了一遍

执行某个命令的时候:

...
An error occurred while installing unf_ext (0.0.7.7), and Bundler cannot continue.
Make sure that `gem install unf_ext -v '0.0.7.7' --source 'https://rubygems.org/'` succeeds before bundling.
...

按提示操作:

$ gem install unf_ext -v '0.0.7.7' --source 'https://rubygems.org/'
Building native extensions. This could take a while...
ERROR:  Error installing unf_ext:
        ERROR: Failed to build gem native extension.

    current directory: ~/.rbenv/versions/2.7.4/lib/ruby/gems/2.7.0/gems/unf_ext-0.0.7.7/ext/unf_ext
~/.rbenv/versions/2.7.4/bin/ruby -I ~/.rbenv/versions/2.7.4/lib/ruby/2.7.0 -r ./siteconf20211003-80002-s3hkjk.rb extconf.rb
checking for -lstdc++... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

...

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  ~/.rbenv/versions/2.7.4/lib/ruby/gems/2.7.0/extensions/x86_64-darwin-17/2.7.0/unf_ext-0.0.7.7/mkmf.log

...

$ cat ~/.rbenv/versions/2.7.4/lib/ruby/gems/2.7.0/extensions/x86_64-darwin-17/2.7.0/unf_ext-0.0.7.7/mkmf.log
" clang -o conftest -I~/.rbenv/versions/2.7.4/include/ruby-2.7.0/x86_64-darwin17 -I~/.rbenv/versions/2.7.4/include/ruby-2.7.0/ruby/backward -I~/.rbenv/versions/2.7.4/include/ruby-2.7.0 -I. -I~/.rbenv/versions/2.7.4/include -I/usr/local/opt/readline/include -I/usr/local/opt/[email protected]/include  -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT    -O3 -Wno-error=shorten-64-to-32  -fno-common -pipe conftest.c  -L. -L~/.rbenv/versions/2.7.4/lib -L. -L~/.rbenv/versions/2.7.4/lib -L/usr/local/opt/readline -L/usr/local/opt/[email protected]/lib  -fstack-protector-strong -L/usr/local/opt/readline/lib -L/usr/local/lib     -lruby.2.7   "
In file included from conftest.c:1:
In file included from ~/.rbenv/versions/2.7.4/include/ruby-2.7.0/ruby.h:33:
In file included from ~/.rbenv/versions/2.7.4/include/ruby-2.7.0/ruby/ruby.h:29:
~/.rbenv/versions/2.7.4/include/ruby-2.7.0/ruby/defines.h:126:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
         ^~~~~~~~~
1 error generated.
checked program was:
/* begin */
1: #include "ruby.h"
2:
3: int main(int argc, char **argv)
4: {
5:   return !!argv[argc];
6: }
/* end */

头文件找不到很常见,刚开始并未在意。

别人解决这个问题无非是重装 macos sdk / command line tools / xcode,再麻烦一点就是手动设置 CPATH / CFLAGS / CPPFLAGS…,结果到我这里全都无效。

我几乎把谷歌找到的帖子都看了一遍,也没找到一个特例。

最后临时禁了 Nix 才得以解决:

$ sh -c 'export PATH=$(echo $PATH | sed -n "s!$HOME/.nix-profile/bin:!!gp" | sed -n "s!/nix/var/nix/profiles/default/bin:!!gp"); bash'

$$ which clang
/usr/bin/clang

$$ gem install unf_ext -v '0.0.7.7'
Fetching unf_ext-0.0.7.7.gem
Building native extensions. This could take a while...
Successfully installed unf_ext-0.0.7.7
Parsing documentation for unf_ext-0.0.7.7
Installing ri documentation for unf_ext-0.0.7.7
Done installing documentation for unf_ext after 0 seconds
1 gem installed
2 个赞

nix 一般不会去全局来装编译器的(或者说把编译器这一类工具导入到 PATH 里),都是针对项目来配置。包括在 nix 里用 gem 也是,不会去做全局/用户环境安装的。针对项目内你要直接用 gem install 也比较麻烦,至少需要把 nix-shell 的环境导出来才可以,一般是用额外工具来把所有依赖生成 nix expression 全部用 nix 来管理。而且也不太需要 rbenv。

nix 为了做到把东西全扔进 /nix/store 和 reproducable 有非常多的 hack。所有库不是走的 /usr/lib 这一类,而且 clang 本身这个包分 output 的,只有 bin 的情况环境下是没有 lib 和 include 的(这个操作在项目里比如 default.nix 这种文件,会有各种 wrap 和 hook 来自动处理这些)。准确来说,分 output 时,会有一个类似 outputsToInstall 的东西来控制导出哪个 output。在 drv 下,默认是带所有的,关联文档,这也就是为什么你上面没有找到 stdio.h

如果你想同时用 nix 和其它包管理,或是有全局环境,建议你不要用 nix-env 或 home-manager 或 nix-darwin 来在全局装任何与开发环境相关的东西(实际上使用大部分也不会这样装,除非你只要那个 bin 而不要其它的东西)。在使用 nix 的场景下,对项目进行写 nix expression 和 shell (或者是现在 unstable 的 flake 也有一样的东西) 后,用 direnv 等手段把环境导到当前工作 shell 里,就能得到只在这个项目下所使用的工具了(哪怕你的工具多个项目都用,比如 lsp server,仍然推荐不要全局装,毕竟可能会和编译器版本相关,如 haskell language server)。

当你不是一个完整的项目,而只是一个文件的时候,可以用 nix-shell 的 shebang,而开发环境可以随手写一个 shell.nix 来整出来。

1 个赞