Memo‎ > ‎

Python + Boost + CMake: Much easier than Boost.Build

posted Aug 17, 2013, 4:29 PM by Teng-Yok Lee   [ updated May 1, 2015, 8:07 AM ]
Using the b2/bjam in Boost.Build is not trivial. Multiple files must be configured, and the documents are not very clear. To the contrast, using CMake is much easier (and with much better support and documents). Everything can be configured  in a single CMakeLists.txt.

The following steps assume that python/boost are not available yet.

Install WinPython (for Windows)

On windows platform, I prefer WinPython since it has more built-in libraries (esp. numpy/scipy/matplolib). Download the latest release of WinPython from its official site: https://winpython.github.io/.

Run the downloaded executable .exe. It will ask where to store the files. The path cannot have space characters.

In my case, I use C:\Users\<user>\Documents where <user> is leeten. The path of WinPython is C:/Users/leeten/Documents/WinPython-64bit-2.7.6.4. The folder with python27.exe is C:/Users/leeten/Documents/WinPython-64bit-2.7.6.4/python-2.7.6.amd64

Configure python for boost


Create a file user-config.jam under C:\Users\<user>. The content should be


# REF: http://www.boost.org/doc/libs/1_58_0/libs/python/doc/building.html
# The content is modified based on the solution of:


# REF: http://stackoverflow.com/questions/15881771/boost-1-53-python-fatal-error-lnk1104-boost-python-vc110-mt-gd-1-53-lib

# Configure specific Python version.

 using python : 2.7

 : C:/Users/leeten/Documents/WinPython-64bit-2.7.6.4/python-2.7.6.amd64/python.exe

 : C:/Users/leeten/Documents/WinPython-64bit-2.7.6.4/python-2.7.6.amd64/include #directory that contains pyconfig.h

 : C:/Users/leeten/Documents/WinPython-64bit-2.7.6.4/python-2.7.6.amd64/libs    #directory that contains python27.lib

 : <toolset>msvc ;


Build boost

Download boost. Note that boost must be built in the final installation location, which is called <boost_dir> hereafter. Then run the following 2 commands:

$ bootstrap

$ b2 --with-python --toolset=msvc-10.0 architecture=x86 address-model=64 link=shared


Setup CMakeLists.txt

In the project that intends to use boost.Python to create the python binding, its CMakeLists.txt file should configure the Python libraries and boost.

PythonLibs:


set(PYTHON_LIBRARY        "C:/Users/leeten/Documents/WinPython-64bit-2.7.6.4/python-2.7.6.amd64/libs/python27.lib"    CACHE FILEPATH "The file path to the python library.")
set(PYTHON_INCLUDE_DIR    "C:/Users/leeten/Documents/WinPython-64bit-2.7.6.4/python-2.7.6.amd64/include"            CACHE PATH "The path to the headers of Python.")

FIND_PACKAGE(PythonLibs)
IF(PYTHONLIBS_FOUND)
  INCLUDE_DIRECTORIES("${PYTHON_INCLUDE_DIRS}")
ELSE()
  MESSAGE(FATAL_ERROR "Unable to find PythonLibs.")
ENDIF()

Boost:


FIND_PACKAGE(Boost)
IF(Boost_FOUND)
  INCLUDE_DIRECTORIES("${Boost_INCLUDE_DIRS}")
  SET(Boost_USE_STATIC_LIBS     OFF)
  SET(Boost_USE_MULTITHREADED    ON)
  SET(Boost_USE_STATIC_RUNTIME     OFF)
  FIND_PACKAGE(Boost  ${BOOST_VERSION}    COMPONENTS python) 
ELSEIF(NOT Boost_FOUND)
  MESSAGE(FATAL_ERROR "Unable to find Boost.")
ENDIF()


Other settings

First, the library should be created as a MODULE.
ADD_LIBRARY(libname MODULE  src1 src2 ...)
Second, the output .dll should have a .pyd extension.
set_target_properties(libname
    PROPERTIES
    SUFFIX ".pyd")

Run CMake

Open CMake for the source folder. Show advanced variables and set both Boost_DIR and Boost_INCLUDE_DIR as <boost_dir>. Config the cmake. It should detect Boost_LIBRARY_DIR, Boost_PYTHON_LIBRARY_DEBUG, and Boost_PYTHON_LIBRARY_RELEASE. Then generate the makefile or VC solutions/projects.

Include Python.h without _DEBUG

When including Python.h, only use Release mode (unless python27_d.lib is available). This can be done as follows:

# REF: http://bytes.com/topic/python/answers/101537-vc-linking-problem
#if    defined(_DEBUG)

    #define WITH_DEBUG   
    #undef _DEBUG
#endif
#include <Python.h>   
#if    defined(WITH_DEBUG)
    #define _DEBUG
#endif

Now you should be able to use boost.python to build the python binding of a C++ library.

My CMake example

SET(MODULE_NAME    sat_dwt_decoder)
PROJECT(${MODULE_NAME}_py)

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# Find other application-dependent packages
FIND_PACKAGE(ThirdPartyLib)

set(PYTHON_LIBRARY        "C:/Python27/libs/python27.lib"    CACHE FILEPATH "The file path to the python library.")
set(PYTHON_INCLUDE_DIR    "C:/Python27/include"            CACHE PATH "The path to the headers of Python.")
FIND_PACKAGE(PythonLibs)
IF(PYTHONLIBS_FOUND)
  INCLUDE_DIRECTORIES("${PYTHON_INCLUDE_DIRS}")
ELSE()
  MESSAGE(FATAL_ERROR "Unable to find PythonLibs.")
ENDIF()

FIND_PACKAGE(Boost)
IF(Boost_FOUND)
  INCLUDE_DIRECTORIES("${Boost_INCLUDE_DIRS}")
  SET(Boost_USE_STATIC_LIBS     OFF)
  SET(Boost_USE_MULTITHREADED    ON)
  SET(Boost_USE_STATIC_RUNTIME     OFF)
  FIND_PACKAGE(Boost  ${BOOST_VERSION}    COMPONENTS python) 
ELSEIF(NOT Boost_FOUND)
  MESSAGE(FATAL_ERROR "Unable to find Boost.")
ENDIF()

# include other source codes.
ADD_LIBRARY(${MODULE_NAME} MODULE 
    sat_dwt_decoder_py.cpp
    )

set_target_properties(${MODULE_NAME}
    PROPERTIES
    SUFFIX ".pyd")
   
TARGET_LINK_LIBRARIES(${MODULE_NAME}
    ${Boost_LIBRARIES}
    ${PYTHON_LIBRARIES}
    ${ThirdPartyLib_LIBRARIES}
    )


References

My CMake is based on the example provided from pseudorandom noise.

Renaming .dll to .pyd is based on the discussions below:
http://www.cmake.org/pipermail/cmake/2006-January/007844.html
http://www.mail-archive.com/cmake@cmake.org/msg14835.html
Comments