From c54753d1068f52911330e46bb7a7131fe1161a81 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Fri, 25 Nov 2016 02:27:12 -0500 Subject: [PATCH] now can build and install with cmake --- CMakeLists.txt | 16 ++++- cmake/GtsamCythonWrap.cmake | 136 ++++++++++++++++++++++++++++++++++++ cython/CMakeLists.txt | 4 ++ cython/build.sh | 11 --- cython/setup.py | 18 ----- cython/setup.py.in | 17 +++++ gtsam/CMakeLists.txt | 9 +++ 7 files changed, 179 insertions(+), 32 deletions(-) create mode 100644 cmake/GtsamCythonWrap.cmake create mode 100644 cython/CMakeLists.txt delete mode 100755 cython/build.sh delete mode 100644 cython/setup.py create mode 100644 cython/setup.py.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c93264f3..57c2b27a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,11 +73,12 @@ option(GTSAM_TANGENT_PREINTEGRATION "Use new ImuFactor with integration on # Options relating to MATLAB wrapper # TODO: Check for matlab mex binary before handling building of binaries option(GTSAM_INSTALL_MATLAB_TOOLBOX "Enable/Disable installation of matlab toolbox" OFF) -option(GTSAM_BUILD_WRAP "Enable/Disable building of matlab wrap utility (necessary for matlab interface)" ON) +option(GTSAM_INSTALL_CYTHON_TOOLBOX "Enable/Disable installation of Cython toolbox" OFF) +option(GTSAM_BUILD_WRAP "Enable/Disable building of matlab/cython wrap utility (necessary for matlab/cython interface)" ON) # Check / set dependent variables for MATLAB wrapper -if(GTSAM_INSTALL_MATLAB_TOOLBOX AND NOT GTSAM_BUILD_WRAP) - message(FATAL_ERROR "GTSAM_INSTALL_MATLAB_TOOLBOX is enabled, please also enable GTSAM_BUILD_WRAP") +if((GTSAM_INSTALL_MATLAB_TOOLBOX OR GTSAM_INSTALL_CYTHON_TOOLBOX) AND NOT GTSAM_BUILD_WRAP) + message(FATAL_ERROR "GTSAM_INSTALL_MATLAB_TOOLBOX or GTSAM_INSTALL_CYTHON_TOOLBOX is enabled, please also enable GTSAM_BUILD_WRAP") endif() if(GTSAM_INSTALL_WRAP AND NOT GTSAM_BUILD_WRAP) message(FATAL_ERROR "GTSAM_INSTALL_WRAP is enabled, please also enable GTSAM_BUILD_WRAP") @@ -378,6 +379,11 @@ if (GTSAM_BUILD_PYTHON) endif() +# Cython wrap +if (GTSAM_INSTALL_CYTHON_TOOLBOX) + add_subdirectory(cython) +endif() + # Build gtsam_unstable if (GTSAM_BUILD_UNSTABLE) add_subdirectory(gtsam_unstable) @@ -509,6 +515,10 @@ endif() if(GTSAM_BUILD_PYTHON) message(STATUS " Python version : ${GTSAM_PYTHON_VERSION}") endif() + +message(STATUS "Cython toolbox flags ") +print_config_flag(${GTSAM_INSTALL_CYTHON_TOOLBOX} "Install Cython toolbox ") +print_config_flag(${GTSAM_BUILD_WRAP} "Build Wrap ") message(STATUS "===============================================================") # Print warnings at the end diff --git a/cmake/GtsamCythonWrap.cmake b/cmake/GtsamCythonWrap.cmake new file mode 100644 index 000000000..e4bed61f2 --- /dev/null +++ b/cmake/GtsamCythonWrap.cmake @@ -0,0 +1,136 @@ +# Set up cache options +set(GTSAM_CYTHON_INSTALL_PATH "" CACHE PATH "Cython toolbox destination, blank defaults to CMAKE_INSTALL_PREFIX/gtsam_cython") +if(NOT GTSAM_CYTHON_INSTALL_PATH) + set(GTSAM_CYTHON_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}/gtsam_cython") +endif() + +# User-friendly wrapping function. Builds a mex module from the provided +# interfaceHeader. For example, for the interface header gtsam.h, +# this will build the wrap module 'gtsam'. +# +# Arguments: +# +# interfaceHeader: The relative path to the wrapper interface definition file. +# linkLibraries: Any *additional* libraries to link. Your project library +# (e.g. `lba`), libraries it depends on, and any necessary +# MATLAB libraries will be linked automatically. So normally, +# leave this empty. +# extraIncludeDirs: Any *additional* include paths required by dependent +# libraries that have not already been added by +# include_directories. Again, normally, leave this empty. +function(wrap_and_install_library_cython interfaceHeader linkLibraries extraIncludeDirs) + wrap_library_internal_cython("${interfaceHeader}" "${linkLibraries}" "${extraIncludeDirs}") + install_wrapped_library_internal_cython("${interfaceHeader}") +endfunction() + + +# Internal function that wraps a library and compiles the wrapper +function(wrap_library_internal_cython interfaceHeader linkLibraries extraIncludeDirs) + # Wrap codegen interface + #usage: wrap --cython interfacePath moduleName toolboxPath headerPath + # interfacePath : *absolute* path to directory of module interface file + # moduleName : the name of the module, interface file must be called moduleName.h + # toolboxPath : the directory in which to generate the wrappers + # headerPath : path to matlab.h + + # Extract module name from interface header file name + get_filename_component(interfaceHeader "${interfaceHeader}" ABSOLUTE) + get_filename_component(modulePath "${interfaceHeader}" PATH) + get_filename_component(moduleName "${interfaceHeader}" NAME_WE) + + # Paths for generated files + set(generated_files_path "${PROJECT_BINARY_DIR}/cython/${moduleName}") + set(generated_cpp_file "${generated_files_path}/${moduleName}.cpp") + + message(STATUS "Building wrap module ${moduleName}") + + # Set up generation of module source file + file(MAKE_DIRECTORY "${generated_files_path}") + configure_file(../cython/setup.py.in ${generated_files_path}/setup.py) + add_custom_command( + OUTPUT ${generated_cpp_file} + DEPENDS ${interfaceHeader} wrap ${module_library_target} + COMMAND + wrap --cython + ${modulePath} + ${moduleName} + ${generated_files_path} + . + && python setup.py build_ext --inplace + VERBATIM + WORKING_DIRECTORY ${generated_files_path}) + + # Set up building of mex module + add_custom_target(${moduleName}_cython_wrapper ALL DEPENDS ${generated_cpp_file} ${interfaceHeader}) + add_custom_target(wrap_${moduleName}_cython_distclean + COMMAND cmake -E remove_directory ${generated_files_path}) +endfunction() + +# Internal function that installs a wrap toolbox +function(install_wrapped_library_internal_cython interfaceHeader) + get_filename_component(moduleName "${interfaceHeader}" NAME_WE) + set(generated_files_path "${PROJECT_BINARY_DIR}/cython/${moduleName}") + + # NOTE: only installs .pxd and .pyx and binary files (not .cpp) - the trailing slash on the directory name + # here prevents creating the top-level module name directory in the destination. + message(STATUS "Installing Cython Toolbox to ${GTSAM_CYTHON_INSTALL_PATH}") + if(GTSAM_BUILD_TYPE_POSTFIXES) + foreach(build_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${build_type}" build_type_upper) + if(${build_type_upper} STREQUAL "RELEASE") + set(build_type_tag "") # Don't create release mode tag on installed directory + else() + set(build_type_tag "${build_type}") + endif() + # Split up filename to strip trailing '/' in GTSAM_CYTHON_INSTALL_PATH if there is one + get_filename_component(location "${GTSAM_CYTHON_INSTALL_PATH}" PATH) + get_filename_component(name "${GTSAM_CYTHON_INSTALL_PATH}" NAME) + install(DIRECTORY "${generated_files_path}/../" DESTINATION "${location}/${name}${build_type_tag}" + CONFIGURATIONS "${build_type}" + PATTERN "build" EXCLUDE + PATTERN "CMakeFiles" EXCLUDE + PATTERN "Makefile" EXCLUDE + PATTERN "*.cmake" EXCLUDE + PATTERN "*.cpp" EXCLUDE + PATTERN "*.py" EXCLUDE) + endforeach() + else() + install(DIRECTORY "${generated_files_path}/../" DESTINATION ${GTSAM_CYTHON_INSTALL_PATH} + PATTERN "build" EXCLUDE + PATTERN "CMakeFiles" EXCLUDE + PATTERN "Makefile" EXCLUDE + PATTERN "*.cmake" EXCLUDE + PATTERN "*.cpp" EXCLUDE + PATTERN "*.py" EXCLUDE) + endif() +endfunction() + +# Helper function to install Cython scripts and handle multiple build types where the scripts +# should be installed to all build type toolboxes +function(install_cython_scripts source_directory patterns) + set(patterns_args "") + set(exclude_patterns "") + + foreach(pattern ${patterns}) + list(APPEND patterns_args PATTERN "${pattern}") + endforeach() + if(GTSAM_BUILD_TYPE_POSTFIXES) + foreach(build_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${build_type}" build_type_upper) + if(${build_type_upper} STREQUAL "RELEASE") + set(build_type_tag "") # Don't create release mode tag on installed directory + else() + set(build_type_tag "${build_type}") + endif() + # Split up filename to strip trailing '/' in GTSAM_CYTHON_INSTALL_PATH if there is one + get_filename_component(location "${GTSAM_CYTHON_INSTALL_PATH}" PATH) + get_filename_component(name "${GTSAM_CYTHON_INSTALL_PATH}" NAME) + install(DIRECTORY "${source_directory}" DESTINATION "${location}/${name}${build_type_tag}" CONFIGURATIONS "${build_type}" + FILES_MATCHING ${patterns_args} PATTERN "${exclude_patterns}" EXCLUDE) + endforeach() + else() + install(DIRECTORY "${source_directory}" DESTINATION "${GTSAM_CYTHON_INSTALL_PATH}" FILES_MATCHING ${patterns_args} PATTERN "${exclude_patterns}" EXCLUDE) + endif() + +endfunction() + diff --git a/cython/CMakeLists.txt b/cython/CMakeLists.txt new file mode 100644 index 000000000..49623f2b9 --- /dev/null +++ b/cython/CMakeLists.txt @@ -0,0 +1,4 @@ +# Install cython components +include(GtsamCythonWrap) +# install scripts and tests +install_cython_scripts("${CMAKE_SOURCE_DIR}/cython/" "*.py") diff --git a/cython/build.sh b/cython/build.sh deleted file mode 100755 index a0505936b..000000000 --- a/cython/build.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# exit if any command returns non-zero -set -e -# print out each command for debugging -set -x - -#usage: wrap [--matlab|--cython] absoluteInterfacePath moduleName toolboxPath headerPath -wrap --cython $PWD gtsam $PWD/gtsam ../ - -python setup.py build_ext --inplace -python -m unittest discover diff --git a/cython/setup.py b/cython/setup.py deleted file mode 100644 index 6ae3ac870..000000000 --- a/cython/setup.py +++ /dev/null @@ -1,18 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension -from Cython.Build import cythonize -import eigency - -setup( - ext_modules = cythonize(Extension( - "gtsam", - sources=["gtsam/gtsam.pyx"], - include_dirs = ["/Users/dta-huynh/install/include", - "/Users/dta-huynh/install/include/gtsam/3rdparty/Eigen", - "/usr/local/include"] + eigency.get_includes(include_eigen=False), - - libraries = ['gtsam'], - library_dirs = ["/Users/dta-huynh/install/lib"], - language="c++", - extra_compile_args=["-std=c++11"])), -) \ No newline at end of file diff --git a/cython/setup.py.in b/cython/setup.py.in new file mode 100644 index 000000000..a2ee736c1 --- /dev/null +++ b/cython/setup.py.in @@ -0,0 +1,17 @@ +from distutils.core import setup +from distutils.extension import Extension +from Cython.Build import cythonize +import eigency + +setup( + ext_modules = cythonize(Extension( + "gtsam", + sources=["gtsam.pyx"], + include_dirs = ["${CMAKE_SOURCE_DIR}", + "${CMAKE_SOURCE_DIR}/gtsam/3rdparty/Eigen" + ] + eigency.get_includes(include_eigen=False), + libraries = ['gtsam'], + library_dirs = ["${CMAKE_BINARY_DIR}/gtsam"], + language="c++", + extra_compile_args=["-std=c++11"])), +) \ No newline at end of file diff --git a/gtsam/CMakeLists.txt b/gtsam/CMakeLists.txt index 8c1d8bb43..a30aab427 100644 --- a/gtsam/CMakeLists.txt +++ b/gtsam/CMakeLists.txt @@ -165,3 +165,12 @@ if (GTSAM_INSTALL_MATLAB_TOOLBOX) # Wrap wrap_and_install_library(../gtsam.h "${GTSAM_ADDITIONAL_LIBRARIES}" "" "${mexFlags}") endif () + +# Create the matlab toolbox for the gtsam library +if (GTSAM_INSTALL_CYTHON_TOOLBOX) + # Set up codegen + include(GtsamCythonWrap) + + # Wrap + wrap_and_install_library_cython(../cython/gtsam.h "${GTSAM_ADDITIONAL_LIBRARIES}" "") +endif () \ No newline at end of file