Using CMake to Build C++ Boost Python Libraries

Overview

The Boost Project has a Python library that makes it easy to seamlessly integrate C++ and Python code. Unfortunately, the documentation recommends using bjam. If you build Boost from source, then this is largely a non-issue. If, however, you are using Boost from preinstalled binaries or from a Gentoo ebuild, then you don't want to use bjam at all!

CMake is an excellent, cross-platform build system tool (e.g., like make but much better). CMake has the ability to determine platform-specific options required to build code. This saves one from having to specify each and every compiler, linker, etc. option including specific system paths, etc. In short, CMake makes is a lot easier to compile and run programs from source.

This post details how to build a minimal C++ library with its code compiled as a module capable of being loaded by Python (version 2.6 or higher) using CMake (version 2.8) and Boost (version 1.45.0). The text below assumes:

  • you are using a BASH shell on a Linux / Unix system,
  • you are using GCC's g++,
  • you already have CMake installed,
  • you already have Boost installed, and,
  • you already have Python installed.

Steps

Step 1: Create Your Development Directory & Set Your PATH

You'll want to create a new directory (e.g., "src") with a "build" subdirectory in it:

$ mkdir -p src/build

and change your current working directory to the new directory:

$ cd src

In order to run the Python code later, also ensure that "." appears in your PATH environment variable:

$ PATH=.:$PATH
$ export PATH

This will permit Python to load the module that is built. You may want to add these two lines to your ~/.bashrc script to avoid having to always set this in any new shells.

Step 2: Create CMakeLists.txt

Now, you can write the file that CMake will use to build the C++ code into a Python module. This module will be compiled as a Shared Object Library file (i.e., a .so file).

NOTE: In Windows, shared objects are Dynamically Linked Libraries (DLLs). Please note that under Linux / Unix, libraries are typically named with a "lib" prefix and a ".so" suffix. Under Windows, libraries simply have a ".dll" suffix. The instructions below are for Linux / Unix and so the "yay" library that is built will be called "libyay.so", whereas, under Windows it would be called "yay.dll". Thus, if you are using Windows, you may have to make some minor adjustments to the files below.

Your CMakeLists.txt file should contain:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
IF(NOT CMAKE_BUILD_TYPE)
  SET(CMAKE_BUILD_TYPE "DEBUG")
  #SET(CMAKE_BUILD_TYPE "RELEASE")
  #SET(CMAKE_BUILD_TYPE "RELWITHDEBINFO")
  #SET(CMAKE_BUILD_TYPE "MINSIZEREL")
ENDIF()

FIND_PACKAGE(Boost 1.45.0)
IF(Boost_FOUND)
  INCLUDE_DIRECTORIES("${Boost_INCLUDE_DIRS}" "/usr/include/python2.6")
  SET(Boost_USE_STATIC_LIBS OFF)
  SET(Boost_USE_MULTITHREADED ON)
  SET(Boost_USE_STATIC_RUNTIME OFF)
  FIND_PACKAGE(Boost 1.45.0 COMPONENTS python)

  ADD_LIBRARY(yay SHARED yay.cxx)
  TARGET_LINK_LIBRARIES(yay ${Boost_LIBRARIES})
ELSEIF(NOT Boost_FOUND)
  MESSAGE(FATAL_ERROR "Unable to find correct Boost version. Did you set BOOST_ROOT?")
ENDIF()

IF(CMAKE_COMPILER_IS_GNUCXX)
  ADD_DEFINITIONS("-Wall")
ELSE()
  MESSAGE(FATAL_ERROR "CMakeLists.txt has not been tested/written for your compiler.")
ENDIF()

Notice that the output will be a shared library file called "yay". It is important to specify both the Boost include path as well as the Python include path with INCLUDE_DIRECTORIES in order for the code to compile properly.

Step 3: Create yay.cxx

Now create yay.cxx:

#include <boost/python.hpp>

char const* yay()
{
  return "Yay!";
}

BOOST_PYTHON_MODULE(libyay)
{
  using namespace boost::python;
  def("yay", yay);
}

It is very important that "libyay" in BOOST_PYTHON_MODULE(libyay) matches the name of the library you're generating in CMakeLists.txt (without the extension).

NOTE: On Linux / Unix systems you will need to prefix the name with "lib" as CMake defaults to prepending the file with "lib" as per convention on Linux / Unix systems.

Essentially, the BOOST_PYTHON_MODULE clause exports the "yay" function as the Python symbol name "yay". This will allow you to call the "yay" function from Python later.

Step 4: Build The Python Module

The best way to invoke CMake is to invoke it in a special build directory. This is why Step 1 mentioned to create a build directory as well. Before you can make the library, you need to have cmake generate the neccessary files to perform the build:

$ cd build
$ cmake ..

(You only need to run "cmake .." after changes are made to CMakeLists.txt.) Now, you can build the program by running make:

$ make

If all went well, you will see a libyay.so file in the current directory! If you need to clean up files, then run "make clean". If the build directory is a complete mess and you want to start over, then remove all of its contents, run "cmake ..", and then "make".

Step 5: Create A Python Script To Call yay()

Now you can test your efforts by writing the following python script:

#!/usr/bin/python
import libyay
print libyay.yay()

When you run it, it should output "Yay" (assuming the library file is somewhere in PATH, or, in the standard location for your system's Python libraries).

Step 6: Have Fun!

That's it! More information on how to export more symbols and call functions (either way) is available in the Boost Python library documentation.

5 Replies to “Using CMake to Build C++ Boost Python Libraries”

  1. Thanks for this article, it was very helpful – I tried using Boost::Python with autotools and am through with that now.
    Is there a nice solution to deal with different versions of python without having to change the version manually?

    1. I am glad it was helpful!

      I’ve not yet explored methods to easily switch (between major versions of) Python when using Boost.Python. If you need to constantly do such (e.g., to build multiple releases off the same code base), then assuming you already have Python installed multiple times (i.e., one location for each version), and you have Boost.Python built and installed into completely independent directories (one for each Python version), then you can use appropriate CMake files to build everything cleanly. Clearly, in this instance, you will need to properly set BOOST_ROOT (before “finding” Boost) for this to work properly. See CMake’s documentation for FindBoost. Also, you may want/need to check out CMake’s documentation on FindPythonInterp, and FindPythonLibs.

      If your goal is to have CMake build all versions with one invocation of CMake, then the strategy would be to use subdirectories to build the independent versions and in the top-level CMakeLists.txt file ensure something like INCLUDE_DIRECTORIES(py26 py30) appears at the bottom. Then you can deal with the different build rules in the CMakeLists.txt files in those respective subdirectories.

    2. Thanks –for most that trick will be sufficient.

      (For those who have multiple versions of Python and many other libraries installed, CMake may not be able to determine the settings and in those cases you will need to set INCLUDE and LIB paths manually.)

Leave a Reply

Your email address will not be published. Required fields are marked *