🐘

[Mac] Catalina/Big Surで、phpenv(php-build)でPHP 7.2以下がインストールできない問題について

2020/12/08に公開

はじめに

phpenvによるPHPのインストールは、インストールしようとするバージョンによって細かい対応が必要な場合があります。

以下のページに本ブログ内の関連記事をまとめていますので、必要に応じてご参照ください🙏

[2020年末版] Macでのphpenv(php-build)によるPHP 5.6〜8.0のインストール方法まとめ

phpenv(php-build)でPHP 7.2以下がインストールできない問題

2020年10月ぐらいから、Macでphpenv(php-build)によるPHP 7.2以下のインストールが失敗するようになりました。(7.3以上は問題なし)

何か環境がおかしいのかなと思いつつ放置していたのですが、最近 止むに止まれぬ事情 によりBig Surをクリーンインストールしまして、気持ちも新たに真っさらな環境でPHPをインストールしようとしたところ、相変わらず7.2以下だけがダメだったので、重い腰を上げて原因を調べました。

調査に丸2日を要しましたが、一応原因が判明して無事にインストールすることができたので、後世のためにブログに書き残しておく次第です💪

再現環境

今回の現象の再現環境は以下のとおりです。

  • macOS Catalina(後期)〜 Big Sur 11.0.1(2020年12月時点の最新)
  • Xcode 12.2(2020年12月時点の最新)
  • phpenv v0.9.0-rc.1(2020年12月時点の最新)
  • php-build v0.11.0dev(2020年12月時点の最新)

インストールコマンドと発生したエラーの内容

実際に打ったインストールコマンドと発生したエラーの内容は以下のとおりでした。

PHP 7.2

インストールコマンド

PHP_BUILD_INSTALL_EXTENSION="apcu=@ imagick=@" PHP_BUILD_EXTRA_MAKE_ARGUMENTS=-j4 PHP_BUILD_CONFIGURE_OPTS="--with-zlib-dir=/usr/local/opt/zlib --with-bz2=/usr/local/opt/bzip2 --with-iconv=/usr/local/opt/libiconv --with-curl=/usr/local/opt/curl --with-libedit=/usr/local/opt/libedit --with-jpeg-dir=/usr/local/opt/libjpeg --with-png-dir=/usr/local/opt/libpng" phpenv install 7.2.34

発生したエラー

[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 7.2.34 into /Users/xxx/.phpenv/versions/7.2.34
[Skipping]: Already downloaded and extracted https://secure.php.net/distributions/php-7.2.34.tar.bz2
[Preparing]: /var/tmp/php-build/source/7.2.34
[Compiling]: /var/tmp/php-build/source/7.2.34

-----------------
|  BUILD ERROR  |
-----------------

Here are the last 10 lines from the log:

-----------------------------------------
2 warnings generated.
/var/tmp/php-build/source/7.2.34/main/reentrancy.c:139:23: error: too few arguments to function call, expected 3, have 2
        readdir_r(dirp, entry);
        ~~~~~~~~~            ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/dirent.h:110:1: note: 'readdir_r' declared here
int readdir_r(DIR *, struct dirent *, struct dirent **) __DARWIN_INODE64(readdir_r);
^
1 error generated.
make: *** [main/reentrancy.lo] Error 1
make: *** Waiting for unfinished jobs....
-----------------------------------------

The full Log is available at '/tmp/php-build.7.2.34.202012xxxxxxxx.log'.
[Warn]: Aborting build.

PHP 7.1

インストールコマンド

# オプションはまったく同じで、指定しているPHPのバージョンが違うだけ
PHP_BUILD_INSTALL_EXTENSION="apcu=@ imagick=@" PHP_BUILD_EXTRA_MAKE_ARGUMENTS=-j4 PHP_BUILD_CONFIGURE_OPTS="--with-zlib-dir=/usr/local/opt/zlib --with-bz2=/usr/local/opt/bzip2 --with-iconv=/usr/local/opt/libiconv --with-curl=/usr/local/opt/curl --with-libedit=/usr/local/opt/libedit --with-jpeg-dir=/usr/local/opt/libjpeg --with-png-dir=/usr/local/opt/libpng" phpenv install 7.1.33

発生したエラー

[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 7.1.33 into /Users/xxx/.phpenv/versions/7.1.33
[Skipping]: Already downloaded and extracted https://secure.php.net/distributions/php-7.1.33.tar.bz2
[Preparing]: /var/tmp/php-build/source/7.1.33
[Compiling]: /var/tmp/php-build/source/7.1.33

-----------------
|  BUILD ERROR  |
-----------------

Here are the last 10 lines from the log:

-----------------------------------------
/var/tmp/php-build/source/7.1.33/Zend/zend_operators.h:128:18: warning: 'finite' is deprecated: first deprecated in macOS 10.9 - Use `isfinite((double)x)` instead. [-Wdeprecated-declarations]
        if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
                        ^
/var/tmp/php-build/source/7.1.33/main/php_config.h:2551:24: note: expanded from macro 'zend_finite'
#define zend_finite(a) finite(a)
                       ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/math.h:752:12: note: 'finite' has been explicitly marked deprecated here
extern int finite(double)
           ^
2 warnings generated.
-----------------------------------------

The full Log is available at '/tmp/php-build.7.1.33.202012xxxxxxxx.log'.
[Warn]: Aborting build.

PHP 7.0

インストールコマンド

# オプションはまったく同じで、指定しているPHPのバージョンが違うだけ
PHP_BUILD_INSTALL_EXTENSION="apcu=@ imagick=@" PHP_BUILD_EXTRA_MAKE_ARGUMENTS=-j4 PHP_BUILD_CONFIGURE_OPTS="--with-zlib-dir=/usr/local/opt/zlib --with-bz2=/usr/local/opt/bzip2 --with-iconv=/usr/local/opt/libiconv --with-curl=/usr/local/opt/curl --with-libedit=/usr/local/opt/libedit --with-jpeg-dir=/usr/local/opt/libjpeg --with-png-dir=/usr/local/opt/libpng" phpenv install 7.0.33

発生したエラー

[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 7.0.33 into /Users/ttskch/.phpenv/versions/7.0.33
[Skipping]: Already downloaded and extracted https://secure.php.net/distributions/php-7.0.33.tar.bz2
[Preparing]: /var/tmp/php-build/source/7.0.33
[Compiling]: /var/tmp/php-build/source/7.0.33

-----------------
|  BUILD ERROR  |
-----------------

Here are the last 10 lines from the log:

-----------------------------------------
/var/tmp/php-build/source/7.0.33/main/php_config.h:2525:5: error: declaration of 'zend_sprintf' has a different language linkage
int zend_sprintf(char *buffer, const char *format, ...);
    ^
/var/tmp/php-build/source/7.0.33/main/php_config.h:2525:5: note: previous declaration is here
int zend_sprintf(char *buffer, const char *format, ...);
    ^
13 warnings and 2 errors generated.
make: *** [ext/intl/intl_convertcpp.lo] Error 1
make: *** Waiting for unfinished jobs....
1 warning generated.
-----------------------------------------

The full Log is available at '/tmp/php-build.7.0.33.202012xxxxxxxx.log'.
[Warn]: Aborting build.

エラーの原因

ズバリ、エラーの原因はどちらも Xcodeのバージョンが新しすぎる ことです。

エラー内容を見ると、

  • PHP 7.2:PHPのソースがコールしている関数のシグネチャがヘッダファイルの関数定義と合っていない
  • PHP 7.1:PHPのソースがdeprecatedな関数をコールしている
  • PHP 7.0:CとC++のリンクの定義? がおかしい?

という内容に見え、

PHP 7.1のエラーについては Xcode Command Line Tools SDKのこのコード がmacOS 10.9以降でdeprecatedだと宣言されているために新しすぎるXcodeではエラーになるという理屈に思えます。(厳密に理解していないので、想像です🙇詳しい方いたらぜひ 教えてください🙏

PHP 7.2のエラーについては以下のリンク先に答えが書いてありました。

Xcode 12から、SDKのclangの設定が変更された( -Werror=implicit-function-declaration というフラグがオンになった)らしく、この影響で7.2以下のPHPのソースがコンパイルできなくなってしまったようです。

Xcode 12 Release Notes | Apple Developer Documentation

Clang now reports an error when you use a function without an explicit declaration when building C or Objective-C code for macOS (-Werror=implicit-function-declaration flag is on). This additional error detection unifies Clang’s behavior for iOS/tvOS and macOS 64-bit targets for this diagnostic. (49917738)

というわけで、結論としては、Xcodeの11系のSDKを使えばPHP 7.2以下もインストールできます。

ちなみに、PHP 7.1のエラーはmacOS 10.9以降でdeprecatedとなっている関数がコールされることが原因ぽいという話でした。
Xcodeのバージョンごとの仕様一覧 を見る限り、Xcode 12.2のターゲットOSは macOS 10.9-11.0 となっていて明らかにダメそう(10.9以上)ですが、Xcode 12のターゲットOSはXcode 11.7と同じで macOS 10.6-10.15.6 となっているので、Xcode 12でもインストールできそうな気がします。
が、実際に試してみたところダメだったので、何か僕の理解が間違っている可能性大です…😓

具体的なインストール方法

今回はXcode 11系の最新版である11.7を使うことにします。

手元で試してみたところ、Xcode 11.7であればPHP 5.6〜PHP 7.2がビルドできることが確認できました。

PHP 5.5ではまた別のdeprecatedエラーが発生したので、さらに古いXcodeが必要なのかもしれませんが、詳しくは未確認です🙏

では、具体的なインストールの手順を説明します。

まず、以下のページからXcodeの11.7をダウンロードします。

https://developer.apple.com/download/more/

Apple IDでのログインが必要で、初回ログイン時に Apple Developer Agreement という画面が出てきてびっくりするかもしれませんが、これは Apple Developer Programとは違って無料での利用についての利用規約 なので、 Submit ボタンを押してもお金はとられません✋

上図のように、xcode 11 などで検索するとすぐに見つけられると思います。11.7のxipファイルをダウンロードしましょう。

ダウンロードしたxipファイルを展開すると、Xcode.app が出てくるので、これを Xcode.11.7.app などにリネームして /Applications に移動させておきましょう。(今後もPHP 7.1/7.2をインストールするときに毎回必要になるので、捨ててしまわずにとっておきたいので)

移動させたら、 Xcode.11.7.app に含まれるSDKを使うようにXcode全体の設定を変更します。

$ sudo xcode-select --switch /Applications/Xcode.11.7.app

参考:Command Line Toolsを切り替える方法 | Sutepulu

これだけでは実際にビルド時に使われるSDKは切り替わらなくて、SDKROOT という環境変数で使うSDKを指定してあげる必要があります。

$ export SDKROOT=macosx
# or
$ export SDKROOT=/Applications/Xcode.11.7.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk

先ほど xcode-select --switch したことによってmacOSのSDKのパスは /Applications/Xcode.11.7.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk に変更されているので、単にSDK名で macosx と指定することができます。( xcodebuild -sdk -version というコマンドでXcodeのSDK一覧を確認できます)

これで、ビルド時にXcode 11.7のSDKが使われるように設定されました。 xcrun --show-sdk-path というコマンドで確認することができます。

$ xcrun --show-sdk-path
/Applications/Xcode.11.7.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk

参考:

これで準備完了です!

あとは普通にインストールコマンドを打てば、今度はインストールが成功するはずです👍

インストールが成功したら、環境をもとに戻しておきましょう。

$ sudo xcode-select --switch /Library/Developer/CommandLineTools/

# 戻ったことを確認
$ xcode-select --print-path
/Library/Developer/CommandLineTools

$ unset SDKROOT

# 戻ったことを確認
$ xcrun --show-sdk-path
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
$ sudo xcode-select --switch /Library/Developer/CommandLineTools/

xcode-select: error: invalid developer directory '/Library/Developer/CommandLineTools/'

というエラーになる場合、App StoreのアップデートでXcocdeのバージョンを上げた後にコマンドラインツールをインストールし直していない可能性が高いです。

この場合は、

$ xcode-select --install

を実行してコマンドラインツールをインストールすれば解決するはずです👌

スクリプト化しておくと便利です

ただでさえ長かったインストールコマンドが、対象のバージョンによってやることが変わってくるところまできてさすがに覚えていられないので、スクリプト化しておくと便利です✋

僕は もともとスクリプト化していた ので、これにバージョンごとの分岐を設けて以下のような内容にしました。ご参考まで。

https://github.com/ttskch/dotfiles/blob/master/bin/phpenv-install

余談1:PHP 7.2で起こっていたエラーについての公式の対応内容

このエラーに関する議論とphp-srcにおける対応は以下にあります。

php-srcの変更はPHP 7.3.24以降にしか入っていないため、それより古いバージョンをインストールする場合はXcode 11系以下が必要になるというのが現状のようです。

余談2:ちなみにSDKだけ差し替えてもダメです

最初、 /Library/Developer/CommandLineTools/SDKs の中を確認してみたところ

$ ls -l /Library/Developer/CommandLineTools/SDKs
total 0
lrwxr-xr-x 1 root wheel  14 12  7 16:41 MacOSX.sdk -> MacOSX11.0.sdk
drwxr-xr-x 8 root wheel 256 12  7 16:41 MacOSX10.15.sdk
drwxr-xr-x 7 root wheel 224 12  7 16:41 MacOSX11.0.sdk

こんなふうに MacOSX10.15.sdkMacOSX11.0.sdk という2つのバージョンのSDKがインストールされていて、 MacOSX11.0.sdk にsymlinkが張られている感じだったので、phracker/MacOSX-SDKs から古いSDKをダウンロードしてきて SDKROOT をそいつにすればコンパイルできるのかな?と思ったのですが、試したらなんか依存が色々見つからないみたいなエラーになってダメでした。

そもそもXcode 11.7に入っているSDKのバージョンは最新とほぼ同じ MacOSX10.15.sdk でしたし、SDKだけ差し替えてもダメでXcode全体の入れ替えが必要みたいですね。(この辺の仕組みは全然厳密に理解してないので雰囲気で話してます🙏)

まとめ

  • macOS Catalina/Big Sur以降でXcodeを最新化している状態だと、phpenv(php-build)でPHP 7.2以下がインストールできない
  • 原因はXcodeが新しすぎること
  • Xcode 11.7を使ってビルドするように設定すればPHP 5.6以上はインストールできるようになる(5.5以下はおそらくさらに古いXcodeが必要)
  • 具体的には、
    1. https://developer.apple.com/download/more/ からXcode 11.7をダウンロード
    2. 展開して /Application/Xcode.11.7.app に配置
    3. sudo xcode-select --switch /Applications/Xcode.11.7.app
    4. export SDKROOT=macosx
    5. phpenv install 7.1.33 (実際には各種コンパイルオプションを環境変数で渡す)
    6. 終わったら sudo xcode-select --switch /Library/Developer/CommandLineTools/ unset SDKROOT でもとに戻す
  • 一連の手順をスクリプト化しておくと便利

以上、お役に立てば幸いです!

GitHubで編集を提案

Discussion