[CMake] Python Extension recipe for CMake

Guilherme Balena Versiani guibv at comunip.com.br
Mon Jan 4 12:36:46 EST 2010


Hello all,

I got stuck while integrating CMake in Ohcount project (patches not 
fully approved yet, but you can see it here 
http://github.com/balena/ohcount). My objective was to create a Python 
Extension from a SWIG-generated source file in a multi-platform fashion. 
To accomplish this, I resolved to unscramble some parts of Python 
libraries ('distutils' for instance), to get the extension name of the 
compiling architecture without using IF(WIN32)/ELIF(UNIX)/.../ELSE(...) 
approach (the if/elif/.../else recipe needs to explicitly declare each 
supported platform in your CMakeLists.txt).

I don't know what you think, but the following recipe could be of use 
for someone else, and maybe included in a macro somewhere else in CMake 
scripts to simplify the building of Python Extensions. Here is the code 
(substitute [package], [path/to/package].i, [dependencies], [link 
libraries] and [target] accordingly to your project):

---------

# Set up Python binding
IF(ENABLE_PYTHON)
 
  MESSAGE(STATUS "Enabled Python binding")
 
  # Find Python executable
  FIND_PACKAGE(PythonLibs REQUIRED)
  FIND_PACKAGE(PythonInterp REQUIRED)
  IF(NOT PYTHONLIBS_FOUND OR NOT PYTHON_EXECUTABLE)
    MESSAGE(SEND_ERROR "You need Python to build Python binding")
  ENDIF(NOT PYTHONLIBS_FOUND OR NOT PYTHON_EXECUTABLE)
 
  FILE(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/python/_*[package]*_)
  FILE(GLOB _python_files "python/*.py")
  FILE(COPY ${_python_files} DESTINATION 
${CMAKE_BINARY_DIR}/python/_*[package]*_)
 
  # SWIG execution ADD_CUSTOM_COMMAND(
    OUTPUT ${CMAKE_BINARY_DIR}/python/_*[package]*__wrap.c
           ${CMAKE_BINARY_DIR}/python/_*[package]*_/_*[package]*_.py
    COMMAND ${SWIG_EXECUTABLE} -python
            -o ${CMAKE_BINARY_DIR}/python/_*_*[package]*_*__wrap.c 
-outdir ${CMAKE_BINARY_DIR}/python/_*_*[package]*_*_
            ${CMAKE_SOURCE_DIR}/_*[path/to/package]*_.i
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/python/_*_*[package]*_*_
    DEPENDS ${CMAKE_SOURCE_DIR}/_*_*[path/to/package]*_*_.i
  )
  # Substitute the [...] below for your include dirs
  INCLUDE_DIRECTORIES(_*[...]*_ ${PYTHON_INCLUDE_DIRS})
  ADD_LIBRARY(_*[target]*_ SHARED 
${CMAKE_BINARY_DIR}/python/_*_*[package]*_*__wrap.c)
  ADD_DEPENDENCIES(_*_*[target]*_*_ _*[dependencies]*_)
  TARGET_LINK_LIBRARIES(_*_*[target]*_*_ ${PYTHON_LIBRARIES} 
_*[link_libraries]*_)
  # The code below prints the Python extension for the current system
  FILE(WRITE "${CMAKE_BINARY_DIR}/getmodsuffix.py"
"import imp
for s in imp.get_suffixes():
    if s[1] == 'rb' and s[0][0] == '.':
        break
print s[0],
"
  ) # Now execute it and remove any newlines from output
  EXECUTE_PROCESS(
    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/getmodsuffix.py
    OUTPUT_VARIABLE _modsuffix
  )
  STRING(REPLACE "\n" "" _modsuffix ${_modsuffix}) # Setup specific 
properties of the Python target
  SET_TARGET_PROPERTIES(_*_*[target]*_*_
   PROPERTIES
      OUTPUT_NAME __*_*_*_*[package]*_*_*_*_       PREFIX "" # There is 
no prefix even on UNIXes
      SUFFIX "${_modsuffix}" # The extension got from Python libraries
      LIBRARY_OUTPUT_DIRECTORY 
${CMAKE_BINARY_DIR}/python/_*_*_*_*[package]*_*_*_*_
      RUNTIME_OUTPUT_DIRECTORY 
${CMAKE_BINARY_DIR}/python/_*_*_*_*[package]*_*_*_*_
  ) # You should set ADDITIONAL_MAKE_CLEAN_FILES using this variable # 
at the end of the CMakeLists.txt file
  LIST(APPEND _additional_clean_files
    "${CMAKE_BINARY_DIR}/python/_*_*_*_*_*_*_*_*[package]*_*_*_*_*_*_*_*__wrap.c" 
# SWIG C wrap file
    "${CMAKE_BINARY_DIR}/python/_*_*_*_*_*_*_*_*[package]*_*_*_*_*_*_*_*_/_*_*_*_*_*_*_*_*[package]*_*_*_*_*_*_*_*_.py" 
# SWIG python wrapper
  )
# If you want, add some unit tests for the generated module this way:   
ADD_TEST(_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*[package]*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_PythonTest 
${CMAKE_COMMAND} -E chdir
    "${CMAKE_SOURCE_DIR}/test/unit/python"
    "${PYTHON_EXECUTABLE}" "python_test.py"
  )
  SET_TESTS_PROPERTIES(PythonTest PROPERTIES
    ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/python"
  )
ENDIF(ENABLE_PYTHON)

---------


Best regards,

--
Guilherme Balena Versiani.


More information about the CMake mailing list