./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
は最適化するかどうかのオプションである。それ以外の細かい注意点について以下の様に纏めた。
macOS / Darwin の場合は --enable-framework
なる Framework 化オプションを選ぶことができる。
多くの場合は /Library/Frameworks
を指定することになるだろうが、今回は使用しない。
Apple の配布している Python 3 では /Applications/Xcode.app/Contents/Developer/Library/Frameworks
を指定している様である。
XQuartz を入れているなど X11 が存在する環境では /opt/X11/include
を参照できないと正しくインストールされない。そこで CFLAGS
として -I/opt/X11/include
を与える様にする。
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 モジュールがないと必要とした際に 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 がインストールされていると分かるので、改めてインストールする必要性はない。
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
原因は configure
が uuid/uuid.h
と uuid.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
を与える様にすれば良い。
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 などのビルドが必要である。
ここではまず 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 モジュールを有効にすることができるだろう。
pip
コマンドはインストールされていない。Python に含まれた機能だけでモジュールを管理する方法を考える。
Python にインストールされたモジュールは次の様にして確認することができる。
python3 -c "help('modules')"
ここにないモジュールは利用できない。また実際に読み込めるかどうかは次の様にして確認することができる。
python3 -c "import <Module Name>"
パスが通っているかどうかの確認にも使えて便利である。Python が実際にモジュールを探すディレクトリは次の様にして確認することができる。
python3 -c "import sys; print (sys.path)"
macOS Catalina では Python 3 はデフォルトでは次の内容になっている。
実際には /Library/Python/3.7/site-packages
も対象である。また macOS Catalina の Python 2 は次の内容になっている。
これらに特徴的なのは常にモジュールを必要としているスクリプトの存在しているディレクトリが優先的に探索される仕様になっているという点である。
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 でモジュールのパスを通す際によく使われる手段は、環境変数 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 はデフォルトでは次の内容になっている。
ユーザがカスタマイズできるのは /Library/Python/3.7/site-packages
だけである。この様に ディレクトリが存在しない場合は sys.path
には追加されないので、サイトの一覧を見るまで本当に利用可能なディレクトリの一覧は判明しない。また macOS Catalina の Python 2 は次の内容になっている。
ユーザがカスタマイズできるのは /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')