diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index a6860f205..9917e8eba 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -18,6 +18,7 @@ install(FILES GtsamMakeConfigFile.cmake GtsamMatlabWrap.cmake GtsamPythonWrap.cmake + GtsamCythonWrap.cmake GtsamTesting.cmake README.html DESTINATION "${SCRIPT_INSTALL_DIR}/GTSAMCMakeTools") diff --git a/cmake/GtsamCythonWrap.cmake b/cmake/GtsamCythonWrap.cmake index e4bed61f2..8f5450988 100644 --- a/cmake/GtsamCythonWrap.cmake +++ b/cmake/GtsamCythonWrap.cmake @@ -4,76 +4,72 @@ 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, +# User-friendly Cython wrapping and installing function. +# Builds a Cython module from the provided interface_header. +# 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}") +# interface_header: The relative path to the wrapper interface definition file. +# extra_imports: extra header to import in the Cython pxd file. +# For example, to use Cython gtsam.pxd in your own module, +# use "from gtsam cimport *" +# setup_py_in_path: Path to the setup.py.in config file, which will be converted +# to setup.py file by cmake and used to compile the Cython module +# by invoking "python setup.py build_ext --inplace" +# install_path: destination to install the library +function(wrap_and_install_library_cython interface_header extra_imports setup_py_in_path install_path) + # Paths for generated files + get_filename_component(module_name "${interface_header}" NAME_WE) + set(generated_files_path "${PROJECT_BINARY_DIR}/cython/${module_name}") + wrap_library_cython("${interface_header}" "${generated_files_path}" "${extra_imports}" "${setup_py_in_path}") + install_cython_wrapped_library("${interface_header}" "${generated_files_path}" "${install_path}") endfunction() # Internal function that wraps a library and compiles the wrapper -function(wrap_library_internal_cython interfaceHeader linkLibraries extraIncludeDirs) +function(wrap_library_cython interface_header generated_files_path extra_imports setup_py_in_path) # 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 path and name from interface header file name + get_filename_component(interface_header "${interface_header}" ABSOLUTE) + get_filename_component(module_path "${interface_header}" PATH) + get_filename_component(module_name "${interface_header}" NAME_WE) + + set(generated_cpp_file "${generated_files_path}/${module_name}.cpp") - # 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}") + message(STATUS "Building wrap module ${module_name}") # Set up generation of module source file file(MAKE_DIRECTORY "${generated_files_path}") - configure_file(../cython/setup.py.in ${generated_files_path}/setup.py) + configure_file(${setup_py_in_path}/setup.py.in ${generated_files_path}/setup.py) add_custom_command( OUTPUT ${generated_cpp_file} - DEPENDS ${interfaceHeader} wrap ${module_library_target} + DEPENDS ${interface_header} wrap COMMAND wrap --cython - ${modulePath} - ${moduleName} + ${module_path} + ${module_name} ${generated_files_path} - . + "${extra_imports}" && 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 + add_custom_target(${module_name}_cython_wrapper ALL DEPENDS ${generated_cpp_file} ${interface_header}) + add_custom_target(wrap_${module_name}_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}") +function(install_cython_wrapped_library interface_header generated_files_path install_path) + get_filename_component(module_name "${interface_header}" NAME_WE) # 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}") + message(STATUS "Installing Cython Toolbox to ${install_path}") #${GTSAM_CYTHON_INSTALL_PATH}") + message(generated_files_path: "${generated_files_path}") if(GTSAM_BUILD_TYPE_POSTFIXES) foreach(build_type ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER "${build_type}" build_type_upper) @@ -83,9 +79,9 @@ function(install_wrapped_library_internal_cython interfaceHeader) 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}" + get_filename_component(location "${install_path}" PATH) + get_filename_component(name "${install_path}" NAME) + install(DIRECTORY "${generated_files_path}/" DESTINATION "${location}/${name}${build_type_tag}" CONFIGURATIONS "${build_type}" PATTERN "build" EXCLUDE PATTERN "CMakeFiles" EXCLUDE @@ -95,7 +91,7 @@ function(install_wrapped_library_internal_cython interfaceHeader) PATTERN "*.py" EXCLUDE) endforeach() else() - install(DIRECTORY "${generated_files_path}/../" DESTINATION ${GTSAM_CYTHON_INSTALL_PATH} + install(DIRECTORY "${generated_files_path}/" DESTINATION ${install_path} PATTERN "build" EXCLUDE PATTERN "CMakeFiles" EXCLUDE PATTERN "Makefile" EXCLUDE diff --git a/gtsam/CMakeLists.txt b/gtsam/CMakeLists.txt index a30aab427..8fb89950c 100644 --- a/gtsam/CMakeLists.txt +++ b/gtsam/CMakeLists.txt @@ -172,5 +172,9 @@ if (GTSAM_INSTALL_CYTHON_TOOLBOX) include(GtsamCythonWrap) # Wrap - wrap_and_install_library_cython(../cython/gtsam.h "${GTSAM_ADDITIONAL_LIBRARIES}" "") + wrap_and_install_library_cython("../cython/gtsam.h" # interface_header + "" # extra imports + "../cython" # path to setup.py.in + "${GTSAM_CYTHON_INSTALL_PATH}/gtsam" # install path + ) endif () \ No newline at end of file