macOS で Python 3 をソースコードからビルドする際の注意点

macOS では長らく Python 2.7.x のみインストールされ、Python 3 はインストールされてこなかったが、Debian など他の環境では Python 3 が一般的になりつつあり、スクリプト開発に支障がある状況が生まれている。 macOS 10.15 Catalina において Python 3.7.3 が導入されたため、最新の macOS はその限りではないが、それ以前の macOS Mojave などでは Python 3 を導入する必要がある。 ここでは一癖ある macOS の Python 3 環境を Homebrew を使用せずに構築する方法について纏める。macOS Catalina では Python 3.7.3 が使用されているので、この記事は Python 3.7.3 を対象とする。

Python 3 のビルド方法

Python 3 を macOS Mojave でビルドする場合は次の様にすれば良い。当然ながら Xcode の Command Line Tools が必要である。
./configure --prefix=<INSTALL PREFIX>/usr --enable-optimizations CFLAGS="-I/opt/X11/include -I$(xcrun --show-sdk-path)/usr/include/uuid -I$(xcrun --show-sdk-path)/usr/include" --with-openssl=<OpenSSL PREFIX>/usr
make -j 4
make install
この様に指定すると次の内容を出力してビルドが完了する。
Python build finished successfully!
The necessary bits to build these optional modules were not found:
_gdbm                 ossaudiodev           spwd               
To find the necessary bits, look in setup.py in detect_modules() for the module's name.


The following modules found by detect_modules() in setup.py, have been
built by the Makefile instead, as configured by the Setup files:
_abc                  atexit                pwd                
time                                                           
このモジュール構成は macOS Catalina の Python 3 とほぼ同一になっている様である。インストールの際は、これも当然であるが、必要に応じて管理者権限を与える。 <INSTALL PREFIX><OpenSSL PREFIX> は適宜変更する。 --enable-optimizations は最適化するかどうかのオプションである。それ以外の細かい注意点について以下の様に纏めた。

Framework 化オプション

macOS / Darwin の場合は --enable-framework なる Framework 化オプションを選ぶことができる。 多くの場合は /Library/Frameworks を指定することになるだろうが、今回は使用しない。 Apple の配布している Python 3 では /Applications/Xcode.app/Contents/Developer/Library/Frameworks を指定している様である。

X11 に関する注意

XQuartz を入れているなど X11 が存在する環境では /opt/X11/include を参照できないと正しくインストールされない。そこで CFLAGS として -I/opt/X11/include を与える様にする。

macOS SDK の利用に関する注意

macOS ではいつの頃からか /usr/include が標準パッケージから削除されるようになった。SDK が必要な場合は Xcode.app 内のヘッダを参照する必要性がある。 Python 3 の configure は SDK ヘッダを明示しないと参照してくれないため、CFLAGS にその情報を与える必要性がある。これらのヘッダが参照できない場合は、次の様なエラーが出て正常にインストールが完了しなくなる。

zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

SDK のパスは xcrun で参照できるため、CFLAGS として -I$(xcrun --show-sdk-path)/usr/include を与える様にする。

sqlite3 モジュールに関する注意

sqlite3 モジュールがないと必要とした際に Python 3 インタプリタが次の様なエラーを起こす。

Traceback (most recent call last):
  File "usr/lib/python3.7/sqlite3/__init__.py", line 23, in <module>
    from sqlite3.dbapi2 import *
  File "usr/lib/python3.7/sqlite3/dbapi2.py", line 27, in <module>
    from _sqlite3 import *
ModuleNotFoundError: No module named '_sqlite3'

これを抑制するには sqlite3 モジュールを含めてビルドせねばならず、後から追加することはできない。 sqlite3 モジュールを含めてビルドするには macOS の SDK ヘッダを参照すれば可能である。SDK ヘッダの指定を忘れると、ヘッダを見つけられないがために sqlite3 モジュールが作成されずに終了する。

また --enable-loadable-sqlite-extensions オプションが存在しているが、これは macOS 標準の sqlite3 を用いる場合は指定してはならない。sqlite3 モジュールを作るにはこのオプションを使う様にするという案内も見かけるが、それは間違いである。 macOS SDK には sqlite3.h, sqlite3ext.h が存在しているが、その sqlite3.h には Modules/_sqlite/connection.c で必要とする sqlite3_enable_load_extension が存在していない。 開発元で配布されているソースコードでは sqlite3.h に次の様に記載されている。

** ^Extension loading is off by default.
** ^Call the sqlite3_enable_load_extension() routine with onoff==1
** to turn extension loading on and call it with onoff==0 to turn
** it back off again.

--enable-loadable-sqlite-extensions オプションはこの説明書きにある sqlite3 の外部モジュールを有効にするためのオプションで、macOS ではデフォルト設定が採用され無効化されている様である。従ってこのオプションを指定すると macOS ではビルド中に次のエラーが出る。

Modules/_sqlite/connection.c:1063:10: error: implicit declaration of function
      'sqlite3_enable_load_extension' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    rc = sqlite3_enable_load_extension(self->db, onoff);
Modules/_sqlite/connection.c:1087:10: error: implicit declaration of function
      'sqlite3_load_extension' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    rc = sqlite3_load_extension(self->db, extension_name, 0, &errmsg);

そして最後に次の様に出る。

Failed to build these modules:
_sqlite3

このオプションは sqlite3 で SQL 文を拡張するために用意されている。指定せずとも sqlite3.h ヘッダが存在していれば問題なくビルドがなされるのでなくても良い。

ちなみに同梱の setup.py によれば、この Python 3.7.3 では sqlite3 の 3.0.8 以降が必要だという。macOS Mojave では sqlite3 --version とすると sqlite3 v3.24.0 がインストールされていると分かるので、改めてインストールする必要性はない。

_uuid モジュールに関する注意

macOS Catalina の Python 3 は _uuid モジュールを認識するが、configure で次のメッセージが出力される。

checking uuid/uuid.h usability... yes
checking uuid/uuid.h presence... yes
checking for uuid/uuid.h... yes
checking uuid.h usability... no
checking uuid.h presence... no
checking for uuid.h... no
checking for uuid_generate_time_safe... no
checking for uuid_create... no
checking for uuid_enc_be... no

このまま続けてビルドしても、次の様な結果となって結局 _uuid モジュールの作成はなされない。

Python build finished successfully!
The necessary bits to build these optional modules were not found:
_uuid

原因は configureuuid/uuid.huuid.h 二つのヘッダを確認するためである。 macOS では uuid/uuid.h のみがインストールされている。uuid.h は古いヘッダなのだそうだが、詳細はよくわからない。 また xcrun を使った macOS SDK ヘッダを明示しない場合は uuid/uuid.h の見つからないといった状況になる。 この問題を回避する単純な解決策は、macOS SDK ヘッダに加えて uuid.h を読み取れる様に、CFLAGS として -I$(xcrun --show-sdk-path)/usr/include/uuid を与える様にすれば良い。

OpenSSL と openssl モジュールに関する注意

SSL に関して次の警告が出ることがある。

The necessary bits to build these optional modules were not found:
_hashlib              _ssl               

Could not build the ssl module!
Python requires an OpenSSL 1.0.2 or 1.1 compatible libssl with X509_VERIFY_PARAM_set1_host().
LibreSSL 2.6.4 and earlier do not provide the necessary APIs, https://github.com/libressl-portable/portable/issues/381

macOS Mojave の LibreSSL は v2.6.5 で条件を満たしている様に見えるが、実際にはエラーが出て openssl モジュールの作成がなされない。LibreSSL v2.7 以降が必要である。 macOS Catalina では LibreSSL v2.8.3 を使用しているので、Mojave においても合わせてインストールしてもよい。その際に使用した PREFIX を --with-openssl オプションに与えるとよい。 Framework 化オプション --enable-framework を使用する場合はインストールする Framework 内に含めてしまっても良いだろう。 Catalina 以降ではシステムの LibreSSL を使用できるが、macOS SDK の特徴から一筋縄ではいかない。ヘッダを別途用意した上で、ライブラリを含んだディレクトリを --with-openssl オプションで指定してやる必要がある。 なお macOS High Sierra では LibreSSL v2.2.7、macOS Sierra までは OpenSSL v0.9.8 系が使用されていたので必ず LibreSSL などのビルドが必要である。

macOS SDK における LibreSSL の利用に関する注意

ここではまず Python 3 とは直接は無関係であるが、Xcode の macOS SDK と libssl.dylib に関する注意点を述べる。 すなわち Python 3 をビルドする際の一つ一つのエラーの原因は基本的に SDK にあるため、その理由を説明しておく。 まず Python 3 の configure で出るヘッダの確認時の警告は、通常の macOS の環境では次の様になっている。

checking for openssl/ssl.h in /usr/local/ssl... no
checking for openssl/ssl.h in /usr/lib/ssl... no
checking for openssl/ssl.h in /usr/ssl... no
checking for openssl/ssl.h in /usr/pkg... no
checking for openssl/ssl.h in /usr/local... no
checking for openssl/ssl.h in /usr... no
checking whether compiling and linking against OpenSSL works... no

この理由は実は単純で、macOS SDK には openssl のヘッダが含まれていないせいである。macOS SDK では LibreSSL の使用を推奨しておらず、Network.framework を推奨しており、LibreSSL は SDK に含まれていない。 しかし LibreSSL ライブラリは全ての macOS でインストールされているので、利用しようと思えば利用できるわけである。そこで例えば macOS Mojave であれば LibreSSL の v2.6.5 のヘッダを持ってくればいいと通常は思うことだろう。 LibreSSL のソースコードからそのまま include を移動させて configure と同じディレクトリに ssl/include の位置に保存する。 そして --with-openssl=./ssl と指定する。CFLAGS に指定しても configure の実装では openssl のヘッダの確認時には反映されないので、--with-openssl=./ssl とするしかない。 ところがここで困ったことが起きる。config.log によれば libssl.dylib へのリンクが通らないためにこのままでは上手くいかない事態が起きる。 更に困ったことに、これは Xcode の仕様であって、clang -lssl -v とする、リンクを試すだけの場合でも次の様になる。

Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch x86_64 -macosx_version_min 10.14.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -o a.out -lssl -L. -L/usr/lib -L/usr/local/lib -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.osx.a
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)

この問題に関して悩んでいる人は多いのではないかと思われる。特に mysql2 のインストールなどで同じ悩みを抱えている様な例を無数に見かけた。不思議なことに ld に対して -lssl -L/usr/lib となっているにも関わらず失敗している。 /usr/lib/libssl.dylib が存在しているにも関わらず、このコードはエラーを吐き出してしまう。-lcurl など別のライブラリならば問題なくリンクできる。この問題は openssl のヘッダが含まれていなかった様に Apple が libssl へのリンクを想定していない為に生じていると考えられる。 ld-L オプションに関わらず、/usr/lib のライブラリを探す場合は SDK の内容を優先する様に設計されていると考えると辻褄があう。 従って、今の場合は $(xcrun --show-sdk-path)/usr/lib における .tbd を見ていると考えられる。SDK にはライブラリのシンボルをテキスト化した .tbd ファイルが含れているが、SDK には libssl.tdb が存在していない為、リンクすることができないというわけである。 この問題を回避するならば、単純にヘッダを保管しているディレクトリに対して /usr/lib へのリンクを設置してやれば良い。すなわち次の様にすればいいだろう。

ln -s /usr/lib ssl/lib

こうしてやれば全ての問題が解決して上手くいく様に思えるが、Python 3 に関しては、そうは問屋が卸さなかった。リンク可能になっても API の互換性がなかったためである。configure は最後に次の警告を吐き出す。

checking for X509_VERIFY_PARAM_set1_host in libssl... no

実際 LibreSSL v2.6.5 のヘッダを見ても X509_VERIFY_PARAM_set1_host はどこにも存在していない様である。これもまた妙な話で、LibreSSL 2.6.4 and earlier do not provide the necessary APIs とあるので、LibreSSL v2.6.5 ならばコンパイルできるはずと思える。 この謎は LibreSSL において脆弱性対応によるマイナーアップデートが実施されたと考えると解決する。LibreSSL v2.6.4 は December 19th, 2017 に公表され、LibreSSL v2.7.0 は March 21st, 2018 に公開された。 LibreSSL v2.6.5 は v2.7 が出た後の June 13th, 2018 に公開されている。Python 3.7.0 はその直後の June 27th, 2018 に公開されているが、おそらく大した問題でもないので、誰も気に留めず、警告文は更新されなかったのだろう。 従って macOS Mojave においては Python 3 で openssl モジュールを有効にするためには必ず LibreSSL v2.7 以降を作る必要性がある。また macOS Catalina 以降で Python 3 を作成する際にはここに示した処方箋を用いれば macOS にインストールされた LibreSSL で openssl モジュールを有効にすることができるだろう。

  1. macでx11/xlib.hの参照
  2. pyenv でcan't decompress data; zlib not availableが出る場合
  3. ソースコードから Python をインストールするときにビルドされないモジュールを確認する
  4. Compiler says uuid.h not found but apt-get says it is
  5. ld: library not found for -lssl
  6. ld: library not found for -lssl で mysql2が Mac OS Mojave でインストールできなかった時のメモ
  7. error installing psycopg2, library not found for -lssl

Python 3 とモジュール管理

Python や Python を利用したスクリプトを使用するならば、適宜モジュールを追加したり、自作モジュールを追加したりしたいことがある。 macOS では pip コマンドはインストールされていない。Python に含まれた機能だけでモジュールを管理する方法を考える。

Python におけるモジュールの探し方

Python にインストールされたモジュールは次の様にして確認することができる。

python3 -c "help('modules')"

ここにないモジュールは利用できない。また実際に読み込めるかどうかは次の様にして確認することができる。

python3 -c "import <Module Name>"

パスが通っているかどうかの確認にも使えて便利である。Python が実際にモジュールを探すディレクトリは次の様にして確認することができる。

python3 -c "import sys; print (sys.path)"

macOS Catalina では Python 3 はデフォルトでは次の内容になっている。

  1. .
  2. /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python37.zip
  3. /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7
  4. /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/lib-dynload
  5. /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/site-packages

実際には /Library/Python/3.7/site-packages も対象である。また macOS Catalina の Python 2 は次の内容になっている。

  1. .
  2. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip
  3. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7
  4. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin
  5. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac
  6. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages
  7. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk
  8. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old
  9. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload
  10. /Library/Python/2.7/site-packages
  11. /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python
  12. /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC

これらに特徴的なのは常にモジュールを必要としているスクリプトの存在しているディレクトリが優先的に探索される仕様になっているという点である。 a/b.py にあるスクリプトが a/c にあるモジュール c を import c している場合、 d/b.py として a/b.py のシンボリックリンクを作り、d/b.py を実行したとしてもモジュールは参照される。 すなわちスクリプトの存在しているディレクトリのパスを解決した上で参照する仕様になっている。

この sys.path は動的に変更でき、優先度は先に指定したディレクトリほど高くなる様になっている。例えば次の様にするとスクリプトの存在しているディレクトリの親ディレクトリが参照される様になる。

import os
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

一つのスクリプトでしか参照しないモジュールはこの様な仕様方法も考えられるが、そうでない場合にはもう少し便利な方法が用意されている。

Python におけるモジュールのパスの通し方とその対象

Python でモジュールのパスを通す際によく使われる手段は、環境変数 PYTHONPATH を指定する方法である。PATH 同様に : で区切る記法で複数のディレクトリを指定できる。例えば .bash_profile.zshenv に次の様に書く。

if [ -d ~/lib/python3 ]; then
	export PYTHONPATH=$HOME/lib/python3:$PYTHONPATH
fi

この様に書けば ~/lib/python3 が探索に追加され、そこにモジュールがあるかどうか Python が確認に行く様になる。環境変数である PYTHONPATH は shell が認識して初めて効果を発揮するため、共用サーバなどではもう少し別の手段を考えたくなるかもしれない。その場合はもちろんやり方が用意されている。

sys.path の探索のリストがどの様に決まっているかというと、これは Python のサイトパッケージによって決定されている様である。次の様にするとサイトの一覧を得ることができる。

python3 -c "import site; print (site.getsitepackages())"

macOS Catalina では Python 3 はデフォルトでは次の内容になっている。

  1. /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/site-packages
  2. /Library/Python/3.7/site-packages
  3. /AppleInternal/Library/Python/3.7/site-packages
  4. /AppleInternal/Tests/Python/3.7

ユーザがカスタマイズできるのは /Library/Python/3.7/site-packages だけである。この様に ディレクトリが存在しない場合は sys.path には追加されないので、サイトの一覧を見るまで本当に利用可能なディレクトリの一覧は判明しない。また macOS Catalina の Python 2 は次の内容になっている。

  1. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages
  2. /System/Library/Frameworks/Python.framework/Versions/2.7/lib/site-python
  3. /Library/Python/2.7/site-packages

ユーザがカスタマイズできるのは /Library/Python/2.7/site-packages だけである。

サイトになっているディレクトリに .pth ファイルを設置すると、Python はその中身を読み取って sys.path に追加する。 例えば /Library/Python/2.7/site-packages のディレクトリには Extras.pth というファイルがあり、そこにファイルパスが列挙されている。 実際にその内容が Python 2 の sys.path に追加されていることが見て取れる。.pth ファイルはパッケージになっているディレクトリでなければならず、例えば sys.path に追加したからといって、.pth ファイルを追加しても参照される様にはならない。

以上を踏まえると macOS で Python のモジュールを取り扱う際には /Library/Python/3.7/site-packages/Library/Python/2.7/site-packages などに入れておけばいい様である。 また例えば /usr/local/lib/python3.7 などに Python のモジュールを入れておきたい場合は、/Library/Python/3.7/site-packages/MyModuleLibraryPath.pth と適当なファイルを作って /usr/local/lib/python3.7 と記載すれば良い。

自作した Python の場合はサイトになっているディレクトリは基本的にインストール時に指定した <PREFIX> に対して <PREFIX>/lib/python3.7/site-packages だけなので、そのディレクトリに .pth ファイルを作って、/Library/Python/3.7/site-packages などを参照する様にしておけば、macOS のものと同じ使い勝手にできる。

.pth ファイルは site モジュールにより読み込まれる。Python インタプリタを起動する際に -S オプションを指定する site モジュールが読み込まれずこの挙動が抑制される様である。 サイトを増やしたい場合は、動的に増やすこともできる。例えば次の様にすると /usr/local/lib/python3.7 がサイトとして追加され、そのディレクトリの .pth ファイルも参照される様になる。

import site

site.addsitedir('/usr/local/lib/python3.7')
  1. site --- サイト固有の設定フック
  2. Using .pth files
技術考󠄁 > macOS で Python 3 をソースコードからビルドする際の注意点
Copyright© R02[2020]. All Rights Reserved.