【puma】puma4台インストールできない問題の対処法

## 環境

Rails 5.2, 6.0
puma 4.3.1, 4.3.4, 4.3.5など
xcode 12.0
xcodeのバージョンは`$ xcodebuild -version`で確認できます。

「puma4台インストールできない問題」というよりは「xcode最新版を入れているとpuma4台インストールできない問題」です。
Rails6で開発をしようとしたところ、以下のようなエラーが出てpumaのインストールができませんでした。

（9/13追記）
puma4.3.6で対応したそうです！
アップデートでこの問題は解決しました。

（11/12追記）
Railsチュートリアルでこの事例が起きる場合があるようです。
Gemfileの以下の記述で`'4.3.6'`に変更すると解決できると思われます。

```rb:Gemfile
gem 'puma', '4.3.6'
```


## エラー文

### $ rails new new_app

```
$ rails new new_app
…（省略）
Fetching bootsnap 1.4.7
Installing bootsnap 1.4.7 with native extensions
Using bundler 2.1.4
Using byebug 11.1.3
Fetching regexp_parser 1.7.1
Installing regexp_parser 1.7.1
Using xpath 3.2.0
Fetching capybara 3.33.0
Installing capybara 3.33.0
Using childprocess 3.0.0
Using ffi 1.13.1
Using jbuilder 2.10.0
Using rb-fsevent 0.10.4
Using rb-inotify 0.10.1
Using listen 3.2.1
Using method_source 1.0.0
Fetching puma 4.3.5
Installing puma 4.3.5 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

current directory:
/Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
/Users/k_end/.rbenv/versions/2.6.6/bin/ruby -I
/Users/k_end/.rbenv/versions/2.6.6/lib/ruby/2.6.0 -r
./siteconf20200729-76891-111jdf1.rb extconf.rb
checking for BIO_read() in -lcrypto... yes
checking for SSL_CTX_new() in -lssl... yes
checking for openssl/bio.h... yes
checking for DTLS_method() in openssl/ssl.h... yes
checking for TLS_server_method() in openssl/ssl.h... yes
checking for SSL_CTX_set_min_proto_version in openssl/ssl.h... yes
creating Makefile

current directory:
/Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
make "DESTDIR=" clean

current directory:
/Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
make "DESTDIR="
compiling http11_parser.c
compiling io_buffer.c
compiling mini_ssl.c
compiling puma_http11.c
puma_http11.c:203:22: error: implicitly declaring library function 'isspace'
with type 'int (int)' [-Werror,-Wimplicit-function-declaration]
  while (vlen > 0 && isspace(value[vlen - 1])) vlen--;
                     ^
puma_http11.c:203:22: note: include the header <ctype.h> or explicitly provide a
declaration for 'isspace'
1 error generated.
make: *** [puma_http11.o] Error 1

make failed, exit code 2

Gem files will remain installed in
/Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5 for
inspection.
Results logged to
/Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-17/2.6.0/puma-4.3.5/gem_make.out

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

In Gemfile:
  puma
         run  bundle binstubs bundler
Could not find gem 'rails (~> 6.0.3, >= 6.0.3.1)' in any of the gem sources
listed in your Gemfile.
         run  bundle exec spring binstub --all
Could not find gem 'rails (~> 6.0.3, >= 6.0.3.1)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.
       rails  webpacker:install
Could not find gem 'rails (~> 6.0.3, >= 6.0.3.1)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.
```

### $ bundle install

```
$ bundle install
…（省略）
Using bundler 2.1.4
Using byebug 11.1.3
Using regexp_parser 1.7.1
Using xpath 3.2.0
Using capybara 3.33.0
Using childprocess 3.0.0
Using ffi 1.13.1
Using jbuilder 2.10.0
Using rb-fsevent 0.10.4
Using rb-inotify 0.10.1
Using listen 3.2.1
Using method_source 1.0.0
Fetching puma 4.3.5
Installing puma 4.3.5 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
/Users/k_end/.rbenv/versions/2.6.6/bin/ruby -I /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/2.6.0 -r ./siteconf20200729-77400-1d4l9f3.rb extconf.rb
checking for BIO_read() in -lcrypto... yes
checking for SSL_CTX_new() in -lssl... yes
checking for openssl/bio.h... yes
checking for DTLS_method() in openssl/ssl.h... yes
checking for TLS_server_method() in openssl/ssl.h... yes
checking for SSL_CTX_set_min_proto_version in openssl/ssl.h... yes
creating Makefile

current directory: /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
make "DESTDIR=" clean

current directory: /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
make "DESTDIR="
compiling http11_parser.c
compiling io_buffer.c
compiling mini_ssl.c
compiling puma_http11.c
puma_http11.c:203:22: error: implicitly declaring library function 'isspace' with type 'int (int)' [-Werror,-Wimplicit-function-declaration]
  while (vlen > 0 && isspace(value[vlen - 1])) vlen--;
                     ^
puma_http11.c:203:22: note: include the header <ctype.h> or explicitly provide a declaration for 'isspace'
1 error generated.
make: *** [puma_http11.o] Error 1

make failed, exit code 2

Gem files will remain installed in /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5 for inspection.
Results logged to /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-17/2.6.0/puma-4.3.5/gem_make.out

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

In Gemfile:
  puma
```

### $ gem install puma -v '4.3.5' --source 'https://rubygems.org/'

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

    current directory: /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
/Users/k_end/.rbenv/versions/2.6.6/bin/ruby -I /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/2.6.0 -r ./siteconf20200729-77587-1dqkt4f.rb extconf.rb
checking for BIO_read() in -lcrypto... yes
checking for SSL_CTX_new() in -lssl... yes
checking for openssl/bio.h... yes
checking for DTLS_method() in openssl/ssl.h... yes
checking for TLS_server_method() in openssl/ssl.h... yes
checking for SSL_CTX_set_min_proto_version in openssl/ssl.h... yes
creating Makefile

current directory: /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
make "DESTDIR=" clean

current directory: /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5/ext/puma_http11
make "DESTDIR="
compiling http11_parser.c
compiling io_buffer.c
compiling mini_ssl.c
compiling puma_http11.c
puma_http11.c:203:22: error: implicitly declaring library function 'isspace' with type 'int (int)' [-Werror,-Wimplicit-function-declaration]
  while (vlen > 0 && isspace(value[vlen - 1])) vlen--;
                     ^
puma_http11.c:203:22: note: include the header <ctype.h> or explicitly provide a declaration for 'isspace'
1 error generated.
make: *** [puma_http11.o] Error 1

make failed, exit code 2

Gem files will remain installed in /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-4.3.5 for inspection.
Results logged to /Users/k_end/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-17/2.6.0/puma-4.3.5/gem_make.out
```

その後、Rubyのバージョンを変えたりbrewのアップデートをしたりRubyを再インストールをしたりopensslを再インストールしたりしてみましたが、解消せず。
しかも、他の人に聞いてみたところ、同じバージョンでも普通にできている。

pumaのバージョンを3台に下げれば動くことはわかったものの、どうにか解決したい。


## 対処法

結論からいうと、Xcodeの最新バージョンがpumaにエラーを投げているらしい。
以下のコマンドで解消した。

```console
$ bundle config build.puma --with-cflags="-Wno-error=implicit-function-declaration"
$ bundle install
```

[fail to bundle install puma 4\.3\.5 or gem puma with ruby\-2\.6\.6 on macos\-10\.15\.6 \- Stack Overflow](https://stackoverflow.com/questions/63187613/fail-to-bundle-install-puma-4-3-5-or-gem-puma-with-ruby-2-6-6-on-macos-10-15-6?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A)

## $ bundle config build.puma --with-cflags="-Wno-error=implicit-function-declaration" とは

とりあえずこれでうまく行ったけど、つまり何をやっているのさ？と思ったので、とりあえず自分の理解の範囲で調べてまとめました。


### `bundle config build.puma`

```console
$ bundle config
Settings are listed in order of priority. The top value will be used.
build.puma
Set for the current user (/Users/k_end/.bundle/config): "--with-cflags=-Wno-error=implicit-function-declaration"

path
Set for your local app (/Users/k_end/workspace/personal/paranoia/.bundle/config): "vendor/bundle"
```

pathに関しては覚えてはいないですが、`$ bundle config set path 'vendor/bundle'`でpathを設定していたのだと思います。

[bundlerで非推奨になった \-\-path \-\-binstubs \- Qiita](https://qiita.com/devzooiiooz/items/8babd82f780f01812f9d)
[Bundlerでビルドオプションを指定する \- Qiita](https://qiita.com/thunders/items/101c6b329830fb1fb27d)


### `--with-cflags`

pumaをビルドするときには、C言語で書かれたファイルをコンパイルしている（エラーでは`compiling puma_http11.c`でこけていた）。
`--with-cflags`オプションを付けることで、puma_http11.cや他のCファイルを`-Wno-error=implicit-function-declaration`オプションを付けてコンパイルします。

[Makefileの書き方メモ \- Qiita](https://qiita.com/nullpo24/items/716bad137f1264b776f5#cflags-)


### `-Wno-error=implicit-function-declaration`

`-Werror-implicit-function-declaration`というのが、「関数が宣言される前に使われている場合にエラーを出」すオプションらしい。そして、「これらの特定の警告オプションのそれぞれには，-Wno-で始まる警告を出力させないための否定形式があります．」ということなので、**関数が宣言される前に使われている場合にエラーを出さない**ということです。
[警告を要求/抑止するオプション - フリーソフトウェア徹底活用講座\(3\)](https://www.cqpub.co.jp/interface/column/freesoft/2002/200210/2.htm)

今回は、`#include <ctype.h>`がされていないよ、というコンパイルエラーが出ているので、それをエラーとして出力しないようにしています。


### 結論

ということで、コンパイル時におけるエラーを発生させないようにしているだけなので、あくまでも応急処置のようなコマンドのようです。
[Explicitly include ctype\.h to fix compilation warning by venables · Pull Request \#2314 · puma/puma](https://github.com/puma/puma/pull/2314)で解決しそうなので、とりあえずマージされるまでは現在のオプションを設定することでしのぎたいと思います。

（9/13追記）
puma4.3.6で対応したそうです！
`bundle config --delete build.puma`で先述の設定は削除することができます。