1.18.2015

C++/Python: How to run Boost.Python module using setup.py with Python3.4 on Mac

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

Related Posts

0 件のコメント:

コメントを投稿