gem ffi のインストールに失敗するときのコンパイラオプション指定方法

2018年12月時点で、Mac で`rails new`した際に ffi gem のインストールに失敗する。エラーメッセージの中で、原因っぽい説明があるのは下記の箇所。

```
# gem のインストールに失敗するとだいたいこれが表示されるが、本質的な解決策からは少し遠いことが多い
An error occurred while installing ffi (1.9.25), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.9.25'` succeeds before bundling.
```

```
# コンパイラがサポートしていないオプションを使っているとのこと
clang: error: unsupported option '-print-multi-os-directory'
clang: error: no input files
```

```
# 指定している引数の値が多すぎるとのこと
libtool:   error: too many parameters to '-version-info'
```

エラーメッセージを見る限りは、違うコンパイラを使うことで解決できそうに思えるが、homebrew を使って libffi をインストールしている環境では、libffi の場所の指定がうまくいっていないことが原因。そのため、`rails new`の際に libffi の場所を正しく指定すれば解決する。

```
# bundle exec rails new your_app の後に bundle install のみ実行する場合
LDFLAGS="-L/usr/local/opt/libffi/lib" PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig" bundle install --path .bundle
```

LDFLAGS と PKG_CONFIG_PATH の場所は、デフォルトなら上記の通りになっているはず。もし libffi を違う場所にインストールしている場合は、下記のコマンドで正しい値を確認できる。

```
$ brew link --force libffi
```

上記のコマンドを実行すると、下記の警告が表示されるが、何も起きないので心配する必要はない。

これは、最新版の homebrew では`brew link`が非推奨になっていることが原因の模様。そのため、`brew link`を実行しても何も起きず、自分で export するかコマンドの実行時に一時的な環境変数をセットする必要がある。（ということが書かれている）

```
Warning: Refusing to link macOS-provided software: libffi
For compilers to find libffi you may need to set:
  export LDFLAGS="-L/usr/local/opt/libffi/lib"

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

ffi gem のリポジトリではこのコンパイラエラーがたびたび issue として報告されているが、根本的な解決にはいまのところ至っていない模様。

- [1.9.25 fails to build in darwin 18](https://github.com/ffi/ffi/issues/647)
- [fails to install: config.sub: too many arguments](https://github.com/ffi/ffi/issues/631)

----

このコンパイラへのオプション指定方法は、libffi 以外にも、エラーが起きがちな openssl でも同じように解決策として利用できる。

```
$ brew link --force openssl

# brew link 環境変数の値を確認した後に、あらためて bundle install すればよい
Warning: Refusing to link macOS-provided software: openssl
If you need to have openssl first in your PATH run:
  echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile

For compilers to find openssl you may need to set:
  export LDFLAGS="-L/usr/local/opt/openssl/lib"
  export CPPFLAGS="-I/usr/local/opt/openssl/include"

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

libffi や openssl と並んでエラーが起きがちな nokogiri gem のインストールが失敗する際は、コンパイラではなく bundler へのオプション指定が必要な点に注意。（最終的にはコンパイラへのオプション指定と同等っぽいですが）

```
# nokogiri gem のインストールに失敗する際の bundle config の一例
bundle config --local build.nokogiri --use-system-libraries
```

ffi gem のインストール失敗時のエラーメッセージ全文は下記の通り。

```
current directory: /Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/gems/ffi-1.9.25/ext/ffi_c
/Users/yourname/.rbenv/versions/2.5.0/bin/ruby -r ./siteconf20181201-36727-45x0fo.rb extconf.rb
checking for ffi.h... no
checking for ffi.h in /usr/local/include,/usr/include/ffi... no
checking for shlwapi.h... no
checking for ruby/thread.h... yes
checking for rb_thread_blocking_region()... no
checking for rb_thread_call_with_gvl()... yes
checking for rb_thread_call_without_gvl()... yes
creating extconf.h
creating Makefile

current directory: /Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/gems/ffi-1.9.25/ext/ffi_c
make "DESTDIR=" clean

current directory: /Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/gems/ffi-1.9.25/ext/ffi_c
make "DESTDIR="
Configuring libffi
clang: error: unsupported option '-print-multi-os-directory'
clang: error: no input files
cd "/Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/gems/ffi-1.9.25/ext/ffi_c/libffi-x86_64-darwin17" && /Library/Developer/CommandLineTools/usr/bin/make
/Library/Developer/CommandLineTools/usr/bin/make 'AR_FLAGS=' 'CC_FOR_BUILD=' 'CFLAGS=-Wall -fexceptions' 'CXXFLAGS=-g -O2' 'CFLAGS_FOR_BUILD=' 'CFLAGS_FOR_TARGET='
'INSTALL=/usr/local/opt/coreutils/libexec/gnubin/install -c' 'INSTALL_DATA=/usr/local/opt/coreutils/libexec/gnubin/install -c -m 644'
'INSTALL_PROGRAM=/usr/local/opt/coreutils/libexec/gnubin/install -c' 'INSTALL_SCRIPT=/usr/local/opt/coreutils/libexec/gnubin/install -c' 'JC1FLAGS=' 'LDFLAGS=' 'LIBCFLAGS='
'LIBCFLAGS_FOR_TARGET=' 'MAKE=/Library/Developer/CommandLineTools/usr/bin/make' 'MAKEINFO=/bin/sh
/Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/gems/ffi-1.9.25/ext/ffi_c/libffi/missing makeinfo ' 'PICFLAG=' 'PICFLAG_FOR_TARGET=' 'RUNTESTFLAGS=' 'SHELL=/bin/sh'
'exec_prefix=/usr/local' 'infodir=/usr/local/share/info' 'libdir=/usr/local/lib' 'mandir=/usr/local/share/man' 'prefix=/usr/local' 'AR=ar' 'AS=as' 'CC=clang' 'CXX=g++' 'LD=ld'
'NM=/usr/local/bin/nm -B' 'RANLIB=ranlib' 'DESTDIR=' all-recursive
Making all in include
make[3]: Nothing to be done for `all'.
Making all in testsuite
make[3]: Nothing to be done for `all'.
Making all in man
make[3]: Nothing to be done for `all'.


###################################
### あまり関係のないエラーログは省略 ###
###################################

libtool:   error: too many parameters to '-version-info'
make[3]: *** [libffi.la] Error 1
make[2]: *** [all-recursive] Error 1
make[1]: *** [all] Error 2
make: *** ["/Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/gems/ffi-1.9.25/ext/ffi_c/libffi-x86_64-darwin17"/.libs/libffi_convenience.a] Error 2

make failed, exit code 2

Gem files will remain installed in /Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/gems/ffi-1.9.25 for inspection.
Results logged to /Users/yourname/Repositories/yourapp/.bundle/ruby/2.5.0/extensions/x86_64-darwin-17/2.5.0-static/ffi-1.9.25/gem_make.out

An error occurred while installing ffi (1.9.25), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.9.25'` succeeds before bundling.

In Gemfile:
  selenium-webdriver was resolved to 3.141.0, which depends on
    childprocess was resolved to 0.9.0, which depends on
      ffi
         run  bundle exec spring binstub --all
rbenv: spring: command not found

The `spring' command exists in these Ruby versions:
  2.3.5
```