macOS Mojaveでgem install ffiに失敗したときのメモ

### TL; DR

```bash
PKG_CONFIG_PATH="/opt/brew/opt/libffi/lib/pkgconfig" gem install ffi
```

---

MacOS Mojaveで `bundle install` したらエラーが起きてインストールに失敗しました．
`nokogiri` のインストールに失敗したのかな...と思ってエラーを見てみると，
どうやらインストールに失敗しているのは `ffi` のようです．

エラーを抜粋します．

```bash
configure: error: C++ preprocessor "/lib/cpp" fails sanity check
See `config.log' for more details
make[1]: *** No targets specified and no makefile found.  Stop.
```

このエラーでグーグル検索をすると， C++ のコンパイラが入っていないのが原因だよ，
g++ を入れてあげれば直るよという記事が目に付きます．
なるほど？と思い手元の環境を確認するも， `brew install gcc` によって C++ のコンパイラは
正常にインストールされているようです．

ここで，困ったなぁと思いながら GitHub の [issue](https://github.com/ffi/ffi/issues)
を眺めに行くと以下の issue が見つかりました．

Fails to install on OSX 10.14 (Mojave): https://github.com/ffi/ffi/issues/651

> sudo ln -s /usr/lib/libSystem.B.dylib /usr/local/lib/libgcc_s.10.4.dylib

で直るよという書き込みがありますが，エラーは解消されませんでした．

更に issue を読み進めると， [libffiのインストールしなおそう](https://github.com/ffi/ffi/issues/651#issuecomment-434927135) という書き込みが見つかります．
これに従い， `brew reinstall libffi` を実行してみます．
すると↓のようなログが出てきました．

```bash
libffi is keg-only, which means it was not symlinked into /opt/brew,
because some formulae require a newer version of libffi.

For compilers to find libffi you may need to set:
  export LDFLAGS="-L/opt/brew/opt/libffi/lib"

For pkg-config to find libffi you may need to set:
  export PKG_CONFIG_PATH="/opt/brew/opt/libffi/lib/pkgconfig"
```

`keg-only` とは，インストールしたライブラリがシステム全体に影響を及ぼす可能性があるため，
homebrew の `lib` などにはリンクを張らないという挙動のことを言うそうです．
(参考: https://qiita.com/halo57/items/e7511f3befbcb9fedd6a)

`ls 'brew --prefix'/lib| grep ffi` (backtickをシングルクオートに変換しています) をしてみると，
確かに ffi に関係しそうな共有ライブラリや静的ライブラリが見当たりません．
一方， 'brew --prefix'/Cellar/libffi/x.x.x/lib` にはライブラリやヘッダファイルがありそうです．

というわけで，よしなにパスを通してあげる必要がありそうです．
さきほど示した [issue](https://github.com/ffi/ffi/issues/651#issuecomment-434927135) を見ると，
`PKG_CONFIG_PATH` と `LDFLAGS` に libffi の lib ディレクトリと pkg-config ファイルを
渡してビルドをしているようです．
実際には， pkg-config がよしなにパスの設定をしてくれるので， `LDFLAGS` の指定は不要です
pkg-config とは， `.pc` という拡張子に記述したヘッダやライブラリのパスに関する設定を読み込んで
必要に応じてフラグを出力してくれたりするツールです．

libffi の pkg-config ファイルを以下に示します．

```bash
prefix=/opt/brew/Cellar/libffi/3.2.1
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
toolexeclibdir=${exec_prefix}/lib
includedir=${libdir}/libffi-3.2.1/include

Name: libffi
Description: Library supporting Foreign Function Interfaces
Version: 3.2.1
Libs: -L${toolexeclibdir} -lffi
Cflags: -I${includedir}
```

libffi がインストールされているパスやライブラリ，ヘッダが置かれているパスが記述されています．
`PKG_CONFIG_PATH` に pkg-config ファイルへのパスを渡すと， `configure` などがそれを読み込み
ビルド時のフラグをよしなにしてくれます．
このため， `PKG_CONFIG_PATH='brew --prefix'/Cellar/libffi/3.2.1/lib/pkgconfig gem install ffi` としてやることでよしなにインストールが成功します（実際には `PKG_CONFIG_PATH` に渡しているのは pkg-config ファイルが存在するディレクトリであることに注意してください）．
以上です，お疲れ様でした．

---

ちなみに， pkg-config はコマンドラインでも使えます．
例えば，ライブラリを使うためのフラグがほしいときには `pkg-config libffi.pc --libs` とします．

```bash
$ pkg-config libffi.pc --libs
-L/opt/brew/Cellar/libffi/3.2.1/lib -lffi
```

ヘッダを使いたいときは `pkg-config libffi.pc --cflags`

```bash
$ pkg-config libffi.pc --cflags
-I/opt/brew/Cellar/libffi/3.2.1/lib/libffi-3.2.1/include
```

両方いっぺんに使うときは↓
```
$ pkg-config libffi.pc --cflags --libs
-I/opt/brew/Cellar/libffi/3.2.1/lib/libffi-3.2.1/include -L/opt/brew/Cellar/libffi/3.2.1/lib -lffi
```