C++/Python: Mac上の Python3.4 で setup.py を使って Boost.Python を動かす方法
バージョン
- Mac: OS X Yosemite Version 10.10.1
- Homebrew: 0.9.5
- pyenv: 20141211
- Python: 3.4.2
- gcc: Apple LLVM version 6.0 (clang-600.0.56)
- Boost: 1.57
環境構築
1. Homebrew最新化
$ sudo brew update
2. pyenv環境整理
$ brew install pyenv
.bashrc/.bash_profile/.zshrc などに追記し実行。
export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)"
$ pyenv install 2.7.9 $ pyenv install 3.4.2 $ pyenv global 3.4.2 2.7.9 $ pyenv rehash $ pyenv versions system * 2.7.9 (set by PYENV_VERSION environment variable) * 3.4.2 (set by PYENV_VERSION environment variable)
3. boost インストール
そのままインストールすると以下のようなエラーが出る。
$ brew install boost-python --with-python3 (snip) ==> Installing boost-python ==> Downloading https://downloads.sourceforge.net/project/boost/boost/1.57.0/boost_1_57_0.tar.bz2 Already downloaded: /Library/Caches/Homebrew/boost-python-1.57.0.tar.bz2 ==> Patching ==> ./bootstrap.sh --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/usr/local/Cellar/boost-python/1.57.0/lib --with-l ==> ./b2 --build-dir=build-python --stagedir=stage-python python=3.4 --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir= ...skipped <pstage-python/lib>libboost_python3.dylib for lack of <pbuild-python/boost/bin.v2/libs/python/build/darwin-4.2.1/release>libboost_python3.dylib... ...skipped <pstage-python/lib>libboost_python.dylib for lack of <pbuild-python/boost/bin.v2/libs/python/build/darwin-4.2.1/release>libboost_python.dylib... ...failed updating 4 targets... ...skipped 4 targets... ...updated 145 targets... READ THIS: http://git.io/brew-troubleshooting
Homebrew のログは ~/Library/Logs/Homebrew/boost-python 配下に出ている。
今回のエラーの原因は、python3.4 ライブラリが見つからなかったためのようだ。
$ grep 'not found' ~/Library/Logs/Homebrew/boost-python/02.b2 ld: library not found for -lpython3.4 ld: library not found for -lpython3.4 ld: library not found for -lpython3.4 ld: library not found for -lpython3.4
暫定策として、libpython3.4m.a を参照する libpython3.4.a を作ったら解決した。
$ ln -s ~/.pyenv/versions/3.4.2/lib/libpython3.4m.a ~/.pyenv/versions/3.4.2/lib/libpython3.4.a $ brew install boost-python --with-python3 ==> Downloading https://downloads.sourceforge.net/project/boost/boost/1.57.0/boost_1_57_0.tar.bz2 Already downloaded: /Library/Caches/Homebrew/boost-python-1.57.0.tar.bz2 ==> Patching ==> ./bootstrap.sh --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/usr/local/Cellar/boost-python/1.57.0/lib --with-libraries=python - ==> ./b2 --build-dir=build-python --stagedir=stage-python python=3.4 --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/usr/local/Cellar ==> ./bootstrap.sh --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/usr/local/Cellar/boost-python/1.57.0/lib --with-libraries=python - ==> ./b2 --build-dir=build-python3 --stagedir=stage-python3 python=3.4 --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/usr/local/Cell /usr/local/Cellar/boost-python/1.57.0: 9 files, 31M, built in 3.3 minutes
ちなみに、system の Python を使うように設定してからインストールすると、今度は python3 が見つからないなどの別のエラーが発生するので注意。
$ pyenv shell system $ brew install boost-python --with-python3 pyenv: python3: command not found The `python3' command exists in these Python versions: 3.4.2 (snip) ==> Downloading https://downloads.sourceforge.net/project/boost/boost/1.57.0/boost_1_57_0.tar.bz2 Already downloaded: /Library/Caches/Homebrew/boost-python-1.57.0.tar.bz2 ==> Patching ==> ./bootstrap.sh --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/usr/local/Cellar/boost-python/1.57.0/lib --with-l ==> ./b2 --build-dir=build-python --stagedir=stage-python python=2.7 --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir= sh: python3: command not found sh: python3: command not found ==> ./bootstrap.sh --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/usr/local/Cellar/boost-python/1.57.0/lib --with-l ==> ./b2 --build-dir=build-python3 --stagedir=stage-python3 python= --prefix=/usr/local/Cellar/boost-python/1.57.0 --libdir=/ /private/tmp/boost-python-QMqTxe/boost_1_57_0/tools/build/src/build-system.jam:583: in load from module build-system (snip)
ディレクトリ構造
ファイルの配置は以下のようにした。
. ├── setup.py ├── src │ └── chello │ └── chello.cpp └── tests ├── __init__.py # 空ファイル └── chello ├── __init__.py # 空ファイル └── test_chello.py
トライ & エラー
最初のバージョンのコードはこのような感じ。
#include <boost/python.hpp> std::string hello() { return "hello world"; } BOOST_PYTHON_MODULE(hello) { using namespace boost::python; def("hello", &hello); }
import unittest from chello import hello class TestCHello(unittest.TestCase): def test_hello(self): self.assertEqual(hello(), 'hello world')
from setuptools import setup, find_packages, Extension setup( name='example-boost-python', version='0.0.1', description='Example for Boost.Python', author='your-name', author_email='your-name@example.com', url='https://example.com/your-repo', install_requires=[], tests_require=[], package_dir={'': 'src'}, packages=find_packages('src'), include_package_data=True, test_suite='tests', entry_points='', ext_modules=[ Extension( name='chello', sources=['src/chello/chello.cpp'], include_dirs=['/usr/local/include/boost'], library_dirs=['/usr/lib', '/usr/local/lib'], libraries=['boost_python3'], extra_compile_args=['-std=c++11', '-Wall'], extra_link_args=[], ) ], )
実行すると、ビルドは問題ないが、テスト実行時に以下のエラーを得る。
$ python3 ./setup.py build (snip) $ python3 ./setup.py test (snip) running build_ext copying build/lib.macosx-10.10-x86_64-3.4/chello.so -> src Traceback (most recent call last): (snip) AttributeError: 'module' object has no attribute 'test_chello'
手で実行すると、モジュールインポート時のエラーであることがわかる。
$ python >>> import src.chello Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: dynamic module does not define init function (PyInit_chello)
モジュールの名前を一致させる
最初の修正は、モジュールの名前を一致させること。hello を chello に修正。
#include <boost/python.hpp> using namespace std; std::string hello() { return "hello world"; } BOOST_PYTHON_MODULE(chello) { using namespace boost::python; def("hello", &hello); }
すると、今度は異なるエラーを得る。
$ python3 ./setup.py test running test running egg_info writing dependency_links to src/example_boost_python.egg-info/dependency_links.txt writing top-level names to src/example_boost_python.egg-info/top_level.txt writing src/example_boost_python.egg-info/PKG-INFO reading manifest file 'src/example_boost_python.egg-info/SOURCES.txt' writing manifest file 'src/example_boost_python.egg-info/SOURCES.txt' running build_ext copying build/lib.macosx-10.10-x86_64-3.4/chello.so -> src Fatal Python error: PyThreadState_Get: no current thread zsh: abort python3 ./setup.py test
ビルドされたファイルの中身を見る。
$ otool -L ./src/chello.so ./src/chello.so: /usr/local/lib/libboost_python.dylib (compatibility version 0.0.0, current version 0.0.0) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
pyenv を system に戻して boost を再インストール
色々試行錯誤したが、結局 system の Python を指定した状態で boost (boost-python ではない) をインストールし直したら動作するようになった。
$ pyenv global system $ brew install python3 # 必要に応じて $ brew rm boost $ brew install boost --build-from-source
$ rm -rf ./build && python3 ./setup.py test (snip) clang -Wno-unused-result -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/usr/local/include -I/usr/local/opt/sqlite/include -I/usr/local/include/boost -I/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/include/python3.4m -c src/chello/chello.cpp -o build/temp.macosx-10.10-x86_64-3.4/src/chello/chello.o -std=c++11 -Wall creating build/lib.macosx-10.10-x86_64-3.4 clang++ -bundle -undefined dynamic_lookup -L/usr/local/lib -L/usr/local/opt/sqlite/lib build/temp.macosx-10.10-x86_64-3.4/src/chello/chello.o -L/usr/lib -L/usr/local/lib -lboost_python3 -o build/lib.macosx-10.10-x86_64-3.4/chello.so copying build/lib.macosx-10.10-x86_64-3.4/chello.so -> src test_hello (tests.chello.test_chello.TestCHello) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.001s OK
この時、動作する環境は system の python3 のみ。
python2 で動かないのはもちろん、pyenv 経由だと先ほどと同じエラーが発生してしまう。
$ python Python 2.7.9 (default, Jan 7 2015, 11:49:12) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import src.chello Traceback (most recent call last): File "<stdin>", line 1, in ImportError: No module named src.chello $ python3 Python 3.4.2 (default, Jan 7 2015, 11:54:58) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import src.chello >>> src.chello.hello() 'hello world' $ pyenv global 3.4.2 2.7.9 $ python3 Python 3.4.2 (default, Jan 18 2015, 02:00:19) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import src.chello Fatal Python error: PyThreadState_Get: no current thread zsh: abort python3
コード
まとめ
$ sudo brew update $ brew [install | upgrade] python3 $ brew install pyenv $ pyenv shell system $ brew install boost --from-source $ brew install boost-python --with-python3 $ python3 ./setup.py build $ python3 ./setup.py test
Boost.Python を利用するプロジェクトでは、pyenv は system を指定する。
References
- Mac - brew install boost に失敗した場合の対処法 - Qiita
- RDKitでFatal Python error: PyThreadState_Get: no current thread
- File: Common-Issues — Documentation for Homebrew/homebrew (master)
0 件のコメント:
コメントを投稿