Merging 'master' into 'wrap'
commit
ad9e8536e7
|
@ -21,20 +21,27 @@ else()
|
||||||
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/cmake")
|
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/cmake")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Install scripts to the standard CMake script directory.
|
# Install CMake scripts to the standard CMake script directory.
|
||||||
install(FILES cmake/gtwrapConfig.cmake cmake/PybindWrap.cmake
|
install(FILES cmake/gtwrapConfig.cmake cmake/MatlabWrap.cmake
|
||||||
cmake/GtwrapUtils.cmake
|
cmake/PybindWrap.cmake cmake/GtwrapUtils.cmake
|
||||||
DESTINATION "${SCRIPT_INSTALL_DIR}/gtwrap")
|
DESTINATION "${SCRIPT_INSTALL_DIR}/gtwrap")
|
||||||
|
|
||||||
|
# Needed for the CMAKE_INSTALL_X variables used below.
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
# Install the gtwrap python package as a directory so it can be found for
|
|
||||||
# wrapping.
|
# Install the gtwrap python package as a directory so it can be found by CMake
|
||||||
|
# for wrapping.
|
||||||
install(DIRECTORY gtwrap DESTINATION "${CMAKE_INSTALL_DATADIR}/gtwrap")
|
install(DIRECTORY gtwrap DESTINATION "${CMAKE_INSTALL_DATADIR}/gtwrap")
|
||||||
|
|
||||||
# Install wrapping scripts as binaries to `CMAKE_INSTALL_PREFIX/bin` so they can
|
# Install wrapping scripts as binaries to `CMAKE_INSTALL_PREFIX/bin` so they can
|
||||||
# be invoked for wrapping.
|
# be invoked for wrapping. We use DESTINATION (instead of TYPE) so we can
|
||||||
install(PROGRAMS scripts/pybind_wrap.py scripts/matlab_wrap.py TYPE BIN)
|
# support older CMake versions.
|
||||||
|
install(PROGRAMS scripts/pybind_wrap.py scripts/matlab_wrap.py
|
||||||
|
DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
# Install pybind11 directory to `CMAKE_INSTALL_PREFIX/lib/pybind11` This will
|
# Install pybind11 directory to `CMAKE_INSTALL_PREFIX/lib/gtwrap/pybind11` This
|
||||||
# allow the gtwrapConfig.cmake file to load it later.
|
# will allow the gtwrapConfig.cmake file to load it later.
|
||||||
install(DIRECTORY pybind11 DESTINATION "${CMAKE_INSTALL_LIBDIR}/gtwrap")
|
install(DIRECTORY pybind11 DESTINATION "${CMAKE_INSTALL_LIBDIR}/gtwrap")
|
||||||
|
|
||||||
|
# Install the matlab.h file to `CMAKE_INSTALL_PREFIX/lib/gtwrap/matlab.h`.
|
||||||
|
install(FILES matlab.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gtwrap")
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
# WRAP
|
# WRAP
|
||||||
|
|
||||||
The wrap library wraps the GTSAM library into a Python library or MATLAB toolbox.
|
The wrap library wraps the GTSAM library into a Python library or MATLAB toolbox.
|
||||||
|
@ -43,36 +42,64 @@ pybind_wrap(${PROJECT_NAME}_py # target
|
||||||
|
|
||||||
For more information, please follow our [tutorial](https://github.com/borglab/gtsam-project-python).
|
For more information, please follow our [tutorial](https://github.com/borglab/gtsam-project-python).
|
||||||
|
|
||||||
## GTSAM Python wrapper
|
## Python Wrapper
|
||||||
|
|
||||||
**WARNING: On macOS, you have to statically build GTSAM to use the wrapper.**
|
**WARNING: On macOS, you have to statically build GTSAM to use the wrapper.**
|
||||||
|
|
||||||
1. Set `GTSAM_BUILD_PYTHON=ON` while configuring the build with `cmake`.
|
1. Set `GTSAM_BUILD_PYTHON=ON` while configuring the build with `cmake`.
|
||||||
1. What you can do in the `build` folder:
|
1. What you can do in the `build` folder:
|
||||||
1. Just run python then import GTSAM and play around:
|
|
||||||
```
|
|
||||||
|
|
||||||
import gtsam
|
1. Just run python then import GTSAM and play around:
|
||||||
gtsam.__dir__()
|
|
||||||
```
|
```
|
||||||
|
import gtsam
|
||||||
|
gtsam.__dir__()
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Run the unittests:
|
||||||
|
```
|
||||||
|
python -m unittest discover
|
||||||
|
```
|
||||||
|
1. Edit the unittests in `python/gtsam/*.py` and simply rerun the test.
|
||||||
|
They were symlinked to `<build_folder>/gtsam/*.py` to facilitate fast development.
|
||||||
|
`python -m unittest gtsam/tests/test_Pose3.py` - NOTE: You might need to re-run `cmake ..` if files are deleted or added.
|
||||||
|
|
||||||
1. Run the unittests:
|
|
||||||
```
|
|
||||||
python -m unittest discover
|
|
||||||
```
|
|
||||||
1. Edit the unittests in `python/gtsam/*.py` and simply rerun the test.
|
|
||||||
They were symlinked to `<build_folder>/gtsam/*.py` to facilitate fast development.
|
|
||||||
```
|
|
||||||
python -m unittest gtsam/tests/test_Pose3.py
|
|
||||||
```
|
|
||||||
- NOTE: You might need to re-run `cmake ..` if files are deleted or added.
|
|
||||||
1. Do `make install` and `cd <gtsam_install_folder>/python`. Here, you can:
|
1. Do `make install` and `cd <gtsam_install_folder>/python`. Here, you can:
|
||||||
1. Run the unittests:
|
1. Run the unittests:
|
||||||
```
|
```
|
||||||
python setup.py test
|
python setup.py test
|
||||||
```
|
```
|
||||||
2. Install `gtsam` to your current Python environment.
|
2. Install `gtsam` to your current Python environment.
|
||||||
```
|
```
|
||||||
python setup.py install
|
python setup.py install
|
||||||
```
|
```
|
||||||
- NOTE: It's a good idea to create a virtual environment otherwise it will be installed in your system Python's site-packages.
|
- NOTE: It's a good idea to create a virtual environment otherwise it will be installed in your system Python's site-packages.
|
||||||
|
|
||||||
|
## Matlab Wrapper
|
||||||
|
|
||||||
|
In the CMake, simply include the `MatlabWrap.cmake` file.
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
include(MatlabWrap)
|
||||||
|
```
|
||||||
|
|
||||||
|
This cmake file defines functions for generating MATLAB wrappers.
|
||||||
|
|
||||||
|
- `wrap_and_install_library(interfaceHeader linkLibraries extraIncludeDirs extraMexFlags)` Generates wrap code and compiles the wrapper.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
`wrap_and_install_library("lba.h" "" "" "")`
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- `interfaceHeader`: The relative or absolute 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.
|
||||||
|
- `extraMexFlags`: Any _additional_ flags to pass to the compiler when building
|
||||||
|
the wrap code. Normally, leave this empty.
|
||||||
|
|
|
@ -0,0 +1,477 @@
|
||||||
|
find_package(
|
||||||
|
Matlab
|
||||||
|
COMPONENTS MEX_COMPILER
|
||||||
|
REQUIRED)
|
||||||
|
|
||||||
|
if(NOT Matlab_MEX_COMPILER)
|
||||||
|
message(
|
||||||
|
FATAL_ERROR
|
||||||
|
"Cannot find MEX compiler binary. Please check your Matlab installation and ensure MEX in installed as well."
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WRAP_BUILD_TYPE_POSTFIXES)
|
||||||
|
set(CURRENT_POSTFIX ${CMAKE_${CMAKE_BUILD_TYPE_UPPER}_POSTFIX})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# WRAP_MEX_BUILD_STATIC_MODULE is not for Windows - on Windows any static
|
||||||
|
# are already compiled into the library by the linker
|
||||||
|
if(WRAP_MEX_BUILD_STATIC_MODULE AND WIN32)
|
||||||
|
message(FATAL_ERROR "WRAP_MEX_BUILD_STATIC_MODULE should not be set on Windows - the linker already automatically compiles in any dependent static libraries. To create a standalone toolbox pacakge, simply ensure that CMake finds the static versions of all dependent libraries (Boost, etc).")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(MEX_COMMAND ${Matlab_MEX_COMPILER} CACHE PATH "Path to MATLAB MEX compiler")
|
||||||
|
set(MATLAB_ROOT ${Matlab_ROOT_DIR} CACHE PATH "Path to MATLAB installation root (e.g. /usr/local/MATLAB/R2012a)")
|
||||||
|
|
||||||
|
# Try to automatically configure mex path from provided custom `bin` path.
|
||||||
|
if(WRAP_CUSTOM_MATLAB_PATH)
|
||||||
|
set(matlab_bin_directory ${WRAP_CUSTOM_MATLAB_PATH})
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(mex_program_name "mex.bat")
|
||||||
|
else()
|
||||||
|
set(mex_program_name "mex")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Run find_program explicitly putting $PATH after our predefined program
|
||||||
|
# directories using 'ENV PATH' and 'NO_SYSTEM_ENVIRONMENT_PATH' - this prevents
|
||||||
|
# finding the LaTeX mex program (totally unrelated to MATLAB Mex) when LaTeX is
|
||||||
|
# on the system path.
|
||||||
|
find_program(MEX_COMMAND ${mex_program_name}
|
||||||
|
PATHS ${matlab_bin_directory} ENV PATH
|
||||||
|
NO_DEFAULT_PATH)
|
||||||
|
mark_as_advanced(FORCE MEX_COMMAND)
|
||||||
|
# Now that we have mex, trace back to find the Matlab installation root
|
||||||
|
get_filename_component(MEX_COMMAND "${MEX_COMMAND}" REALPATH)
|
||||||
|
get_filename_component(mex_path "${MEX_COMMAND}" PATH)
|
||||||
|
if(mex_path MATCHES ".*/win64$")
|
||||||
|
get_filename_component(MATLAB_ROOT "${mex_path}/../.." ABSOLUTE)
|
||||||
|
else()
|
||||||
|
get_filename_component(MATLAB_ROOT "${mex_path}/.." ABSOLUTE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Consistent and user-friendly wrap function
|
||||||
|
function(matlab_wrap interfaceHeader linkLibraries
|
||||||
|
extraIncludeDirs extraMexFlags ignore_classes)
|
||||||
|
wrap_and_install_library("${interfaceHeader}" "${linkLibraries}"
|
||||||
|
"${extraIncludeDirs}" "${extraMexFlags}"
|
||||||
|
"${ignore_classes}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# extraMexFlags: Any *additional* flags to pass to the compiler when building
|
||||||
|
# the wrap code. Normally, leave this empty.
|
||||||
|
# ignore_classes: List of classes to ignore in the wrapping.
|
||||||
|
function(wrap_and_install_library interfaceHeader linkLibraries
|
||||||
|
extraIncludeDirs extraMexFlags ignore_classes)
|
||||||
|
wrap_library_internal("${interfaceHeader}" "${linkLibraries}"
|
||||||
|
"${extraIncludeDirs}" "${mexFlags}")
|
||||||
|
install_wrapped_library_internal("${interfaceHeader}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Internal function that wraps a library and compiles the wrapper
|
||||||
|
function(wrap_library_internal interfaceHeader linkLibraries extraIncludeDirs
|
||||||
|
extraMexFlags)
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(mexModuleExt mexa64)
|
||||||
|
else()
|
||||||
|
set(mexModuleExt mexglx)
|
||||||
|
endif()
|
||||||
|
elseif(APPLE)
|
||||||
|
set(mexModuleExt mexmaci64)
|
||||||
|
elseif(MSVC)
|
||||||
|
if(CMAKE_CL_64)
|
||||||
|
set(mexModuleExt mexw64)
|
||||||
|
else()
|
||||||
|
set(mexModuleExt mexw32)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Wrap codegen interface usage: wrap 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}/wrap/${moduleName}")
|
||||||
|
set(generated_cpp_file "${generated_files_path}/${moduleName}_wrapper.cpp")
|
||||||
|
set(compiled_mex_modules_root "${PROJECT_BINARY_DIR}/wrap/${moduleName}_mex")
|
||||||
|
|
||||||
|
message(STATUS "Building wrap module ${moduleName}")
|
||||||
|
|
||||||
|
# Set matlab.h in project
|
||||||
|
set(matlab_h_path "${CMAKE_SOURCE_DIR}")
|
||||||
|
|
||||||
|
# If building a static mex module, add all cmake-linked libraries to the
|
||||||
|
# explicit link libraries list so that the next block of code can unpack any
|
||||||
|
# static libraries
|
||||||
|
set(automaticDependencies "")
|
||||||
|
foreach(lib ${moduleName} ${linkLibraries})
|
||||||
|
# message("MODULE NAME: ${moduleName}")
|
||||||
|
if(TARGET "${lib}")
|
||||||
|
get_target_property(dependentLibraries ${lib} INTERFACE_LINK_LIBRARIES)
|
||||||
|
# message("DEPENDENT LIBRARIES: ${dependentLibraries}")
|
||||||
|
if(dependentLibraries)
|
||||||
|
list(APPEND automaticDependencies ${dependentLibraries})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# CHRIS: Temporary fix. On my system the get_target_property above returned
|
||||||
|
# Not-found for gtsam module This needs to be fixed!!
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
list(
|
||||||
|
APPEND
|
||||||
|
automaticDependencies
|
||||||
|
${Boost_SERIALIZATION_LIBRARY_RELEASE}
|
||||||
|
${Boost_FILESYSTEM_LIBRARY_RELEASE}
|
||||||
|
${Boost_SYSTEM_LIBRARY_RELEASE}
|
||||||
|
${Boost_THREAD_LIBRARY_RELEASE}
|
||||||
|
${Boost_DATE_TIME_LIBRARY_RELEASE})
|
||||||
|
# Only present in Boost >= 1.48.0
|
||||||
|
if(Boost_TIMER_LIBRARY_RELEASE)
|
||||||
|
list(APPEND automaticDependencies ${Boost_TIMER_LIBRARY_RELEASE}
|
||||||
|
${Boost_CHRONO_LIBRARY_RELEASE})
|
||||||
|
if(WRAP_MEX_BUILD_STATIC_MODULE)
|
||||||
|
# list(APPEND automaticDependencies -Wl,--no-as-needed -lrt)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# message("AUTOMATIC DEPENDENCIES: ${automaticDependencies}") CHRIS: End
|
||||||
|
# temporary fix
|
||||||
|
|
||||||
|
# Separate dependencies
|
||||||
|
set(correctedOtherLibraries "")
|
||||||
|
set(otherLibraryTargets "")
|
||||||
|
set(otherLibraryNontargets "")
|
||||||
|
set(otherSourcesAndObjects "")
|
||||||
|
foreach(lib ${moduleName} ${linkLibraries} ${automaticDependencies})
|
||||||
|
if(TARGET "${lib}")
|
||||||
|
if(WRAP_MEX_BUILD_STATIC_MODULE)
|
||||||
|
get_target_property(target_sources ${lib} SOURCES)
|
||||||
|
list(APPEND otherSourcesAndObjects ${target_sources})
|
||||||
|
else()
|
||||||
|
list(APPEND correctedOtherLibraries ${lib})
|
||||||
|
list(APPEND otherLibraryTargets ${lib})
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
get_filename_component(file_extension "${lib}" EXT)
|
||||||
|
get_filename_component(lib_name "${lib}" NAME_WE)
|
||||||
|
if(file_extension STREQUAL ".a" AND WRAP_MEX_BUILD_STATIC_MODULE)
|
||||||
|
# For building a static MEX module, unpack the static library and
|
||||||
|
# compile its object files into our module
|
||||||
|
file(MAKE_DIRECTORY "${generated_files_path}/${lib_name}_objects")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ar -x "${lib}"
|
||||||
|
WORKING_DIRECTORY "${generated_files_path}/${lib_name}_objects"
|
||||||
|
RESULT_VARIABLE ar_result)
|
||||||
|
if(NOT ar_result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed extracting ${lib}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Get list of object files
|
||||||
|
execute_process(
|
||||||
|
COMMAND ar -t "${lib}"
|
||||||
|
OUTPUT_VARIABLE object_files
|
||||||
|
RESULT_VARIABLE ar_result)
|
||||||
|
if(NOT ar_result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed listing ${lib}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add directory to object files
|
||||||
|
string(REPLACE "\n" ";" object_files_list "${object_files}")
|
||||||
|
foreach(object_file ${object_files_list})
|
||||||
|
get_filename_component(file_extension "${object_file}" EXT)
|
||||||
|
if(file_extension STREQUAL ".o")
|
||||||
|
list(APPEND otherSourcesAndObjects
|
||||||
|
"${generated_files_path}/${lib_name}_objects/${object_file}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
list(APPEND correctedOtherLibraries ${lib})
|
||||||
|
list(APPEND otherLibraryNontargets ${lib})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Check libraries for conflicting versions built-in to MATLAB
|
||||||
|
set(dependentLibraries "")
|
||||||
|
if(NOT "${otherLibraryTargets}" STREQUAL "")
|
||||||
|
foreach(target ${otherLibraryTargets})
|
||||||
|
get_target_property(dependentLibrariesOne ${target}
|
||||||
|
INTERFACE_LINK_LIBRARIES)
|
||||||
|
list(APPEND dependentLibraries ${dependentLibrariesOne})
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
list(APPEND dependentLibraries ${otherLibraryNontargets})
|
||||||
|
check_conflicting_libraries_internal("${dependentLibraries}")
|
||||||
|
|
||||||
|
# Set up generation of module source file
|
||||||
|
file(MAKE_DIRECTORY "${generated_files_path}")
|
||||||
|
|
||||||
|
find_package(PythonInterp ${WRAP_PYTHON_VERSION} EXACT)
|
||||||
|
find_package(PythonLibs ${WRAP_PYTHON_VERSION} EXACT)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${generated_cpp_file}
|
||||||
|
DEPENDS ${interfaceHeader} ${module_library_target} ${otherLibraryTargets}
|
||||||
|
${otherSourcesAndObjects}
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND} -E env
|
||||||
|
"PYTHONPATH=${GTWRAP_PACKAGE_DIR}${GTWRAP_PATH_SEPARATOR}$ENV{PYTHONPATH}"
|
||||||
|
${PYTHON_EXECUTABLE} ${MATLAB_WRAP_SCRIPT} --src ${interfaceHeader}
|
||||||
|
--module_name ${moduleName} --out ${generated_files_path}
|
||||||
|
--top_module_namespaces ${moduleName} --ignore ${ignore_classes}
|
||||||
|
VERBATIM
|
||||||
|
WORKING_DIRECTORY ${generated_files_path})
|
||||||
|
|
||||||
|
# Set up building of mex module
|
||||||
|
string(REPLACE ";" " " extraMexFlagsSpaced "${extraMexFlags}")
|
||||||
|
string(REPLACE ";" " " mexFlagsSpaced "${WRAP_BUILD_MEX_BINARY_FLAGS}")
|
||||||
|
add_library(
|
||||||
|
${moduleName}_matlab_wrapper MODULE
|
||||||
|
${generated_cpp_file} ${interfaceHeader} ${otherSourcesAndObjects})
|
||||||
|
target_link_libraries(${moduleName}_matlab_wrapper ${correctedOtherLibraries})
|
||||||
|
target_link_libraries(${moduleName}_matlab_wrapper ${moduleName})
|
||||||
|
set_target_properties(
|
||||||
|
${moduleName}_matlab_wrapper
|
||||||
|
PROPERTIES OUTPUT_NAME "${moduleName}_wrapper"
|
||||||
|
PREFIX ""
|
||||||
|
SUFFIX ".${mexModuleExt}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${compiled_mex_modules_root}"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${compiled_mex_modules_root}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${compiled_mex_modules_root}"
|
||||||
|
CLEAN_DIRECT_OUTPUT 1)
|
||||||
|
set_property(
|
||||||
|
TARGET ${moduleName}_matlab_wrapper
|
||||||
|
APPEND_STRING
|
||||||
|
PROPERTY
|
||||||
|
COMPILE_FLAGS
|
||||||
|
" ${extraMexFlagsSpaced} ${mexFlagsSpaced} \"-I${MATLAB_ROOT}/extern/include\" -DMATLAB_MEX_FILE -DMX_COMPAT_32"
|
||||||
|
)
|
||||||
|
set_property(
|
||||||
|
TARGET ${moduleName}_matlab_wrapper
|
||||||
|
APPEND
|
||||||
|
PROPERTY INCLUDE_DIRECTORIES ${extraIncludeDirs})
|
||||||
|
# Disable build type postfixes for the mex module - we install in different
|
||||||
|
# directories for each build type instead
|
||||||
|
foreach(build_type ${CMAKE_CONFIGURATION_TYPES})
|
||||||
|
string(TOUPPER "${build_type}" build_type_upper)
|
||||||
|
set_target_properties(${moduleName}_matlab_wrapper
|
||||||
|
PROPERTIES ${build_type_upper}_POSTFIX "")
|
||||||
|
endforeach()
|
||||||
|
# Set up platform-specific flags
|
||||||
|
if(MSVC)
|
||||||
|
if(CMAKE_CL_64)
|
||||||
|
set(mxLibPath "${MATLAB_ROOT}/extern/lib/win64/microsoft")
|
||||||
|
else()
|
||||||
|
set(mxLibPath "${MATLAB_ROOT}/extern/lib/win32/microsoft")
|
||||||
|
endif()
|
||||||
|
target_link_libraries(
|
||||||
|
${moduleName}_matlab_wrapper "${mxLibPath}/libmex.lib"
|
||||||
|
"${mxLibPath}/libmx.lib" "${mxLibPath}/libmat.lib")
|
||||||
|
set_target_properties(${moduleName}_matlab_wrapper
|
||||||
|
PROPERTIES LINK_FLAGS "/export:mexFunction")
|
||||||
|
set_property(
|
||||||
|
SOURCE "${generated_cpp_file}"
|
||||||
|
APPEND
|
||||||
|
PROPERTY COMPILE_FLAGS "/bigobj")
|
||||||
|
elseif(APPLE)
|
||||||
|
set(mxLibPath "${MATLAB_ROOT}/bin/maci64")
|
||||||
|
target_link_libraries(
|
||||||
|
${moduleName}_matlab_wrapper "${mxLibPath}/libmex.dylib"
|
||||||
|
"${mxLibPath}/libmx.dylib" "${mxLibPath}/libmat.dylib")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Hacking around output issue with custom command Deletes generated build
|
||||||
|
# folder
|
||||||
|
add_custom_target(
|
||||||
|
wrap_${moduleName}_matlab_distclean
|
||||||
|
COMMAND cmake -E remove_directory ${generated_files_path}
|
||||||
|
COMMAND cmake -E remove_directory ${compiled_mex_modules_root})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Internal function that installs a wrap toolbox
|
||||||
|
function(install_wrapped_library_internal interfaceHeader)
|
||||||
|
get_filename_component(moduleName "${interfaceHeader}" NAME_WE)
|
||||||
|
set(generated_files_path "${PROJECT_BINARY_DIR}/wrap/${moduleName}")
|
||||||
|
|
||||||
|
# NOTE: only installs .m and mex 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 Matlab Toolbox to ${WRAP_TOOLBOX_INSTALL_PATH}")
|
||||||
|
if(WRAP_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 WRAP_TOOLBOX_INSTALL_PATH if
|
||||||
|
# there is one
|
||||||
|
get_filename_component(location "${WRAP_TOOLBOX_INSTALL_PATH}" PATH)
|
||||||
|
get_filename_component(name "${WRAP_TOOLBOX_INSTALL_PATH}" NAME)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${generated_files_path}/"
|
||||||
|
DESTINATION "${location}/${name}${build_type_tag}"
|
||||||
|
CONFIGURATIONS "${build_type}"
|
||||||
|
FILES_MATCHING
|
||||||
|
PATTERN "*.m")
|
||||||
|
install(
|
||||||
|
TARGETS ${moduleName}_matlab_wrapper
|
||||||
|
LIBRARY DESTINATION "${location}/${name}${build_type_tag}"
|
||||||
|
CONFIGURATIONS "${build_type}"
|
||||||
|
RUNTIME DESTINATION "${location}/${name}${build_type_tag}"
|
||||||
|
CONFIGURATIONS "${build_type}")
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
install(
|
||||||
|
DIRECTORY "${generated_files_path}/"
|
||||||
|
DESTINATION ${WRAP_TOOLBOX_INSTALL_PATH}
|
||||||
|
FILES_MATCHING
|
||||||
|
PATTERN "*.m")
|
||||||
|
install(
|
||||||
|
TARGETS ${moduleName}_matlab_wrapper
|
||||||
|
LIBRARY DESTINATION ${WRAP_TOOLBOX_INSTALL_PATH}
|
||||||
|
RUNTIME DESTINATION ${WRAP_TOOLBOX_INSTALL_PATH})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Internal function to check for libraries installed with MATLAB that may
|
||||||
|
# conflict and prints a warning to move them if problems occur.
|
||||||
|
function(check_conflicting_libraries_internal libraries)
|
||||||
|
if(UNIX)
|
||||||
|
# Set path for matlab's built-in libraries
|
||||||
|
if(APPLE)
|
||||||
|
set(mxLibPath "${MATLAB_ROOT}/bin/maci64")
|
||||||
|
else()
|
||||||
|
if(CMAKE_CL_64)
|
||||||
|
set(mxLibPath "${MATLAB_ROOT}/bin/glnxa64")
|
||||||
|
else()
|
||||||
|
set(mxLibPath "${MATLAB_ROOT}/bin/glnx86")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# List matlab's built-in libraries
|
||||||
|
file(
|
||||||
|
GLOB matlabLibs
|
||||||
|
RELATIVE "${mxLibPath}"
|
||||||
|
"${mxLibPath}/lib*")
|
||||||
|
|
||||||
|
# Convert to base names
|
||||||
|
set(matlabLibNames "")
|
||||||
|
foreach(lib ${matlabLibs})
|
||||||
|
get_filename_component(libName "${lib}" NAME_WE)
|
||||||
|
list(APPEND matlabLibNames "${libName}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Get names of link libraries
|
||||||
|
set(linkLibNames "")
|
||||||
|
foreach(lib ${libraries})
|
||||||
|
string(FIND "${lib}" "/" slashPos)
|
||||||
|
if(NOT slashPos EQUAL -1)
|
||||||
|
# If the name is a path, just get the library name
|
||||||
|
get_filename_component(libName "${lib}" NAME_WE)
|
||||||
|
list(APPEND linkLibNames "${libName}")
|
||||||
|
else()
|
||||||
|
# It's not a path, so see if it looks like a filename
|
||||||
|
get_filename_component(ext "${lib}" EXT)
|
||||||
|
if(NOT "${ext}" STREQUAL "")
|
||||||
|
# It's a filename, so get the base name
|
||||||
|
get_filename_component(libName "${lib}" NAME_WE)
|
||||||
|
list(APPEND linkLibNames "${libName}")
|
||||||
|
else()
|
||||||
|
# It's not a filename so it must be a short name, add the "lib" prefix
|
||||||
|
list(APPEND linkLibNames "lib${lib}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Remove duplicates
|
||||||
|
list(REMOVE_DUPLICATES linkLibNames)
|
||||||
|
|
||||||
|
set(conflictingLibs "")
|
||||||
|
foreach(lib ${linkLibNames})
|
||||||
|
list(FIND matlabLibNames "${lib}" libPos)
|
||||||
|
if(NOT libPos EQUAL -1)
|
||||||
|
if(NOT conflictingLibs STREQUAL "")
|
||||||
|
set(conflictingLibs "${conflictingLibs}, ")
|
||||||
|
endif()
|
||||||
|
set(conflictingLibs "${conflictingLibs}${lib}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT "${conflictingLibs}" STREQUAL "")
|
||||||
|
message(
|
||||||
|
WARNING
|
||||||
|
"The project links to the libraries [ ${conflictingLibs} ] on your system, but "
|
||||||
|
"MATLAB is distributed with its own versions of these libraries which may conflict. "
|
||||||
|
"If you get strange errors or crashes with the MATLAB wrapper, move these "
|
||||||
|
"libraries out of MATLAB's built-in library directory, which is ${mxLibPath} on "
|
||||||
|
"your system. MATLAB will usually still work with these libraries moved away, but "
|
||||||
|
"if not, you'll have to compile the static MATLAB wrapper module."
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Helper function to install MATLAB scripts and handle multiple build types
|
||||||
|
# where the scripts should be installed to all build type toolboxes
|
||||||
|
function(install_matlab_scripts source_directory patterns)
|
||||||
|
set(patterns_args "")
|
||||||
|
set(exclude_patterns "")
|
||||||
|
|
||||||
|
foreach(pattern ${patterns})
|
||||||
|
list(APPEND patterns_args PATTERN "${pattern}")
|
||||||
|
endforeach()
|
||||||
|
if(WRAP_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 WRAP_TOOLBOX_INSTALL_PATH if
|
||||||
|
# there is one
|
||||||
|
get_filename_component(location "${WRAP_TOOLBOX_INSTALL_PATH}" PATH)
|
||||||
|
get_filename_component(name "${WRAP_TOOLBOX_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 "${WRAP_TOOLBOX_INSTALL_PATH}"
|
||||||
|
FILES_MATCHING ${patterns_args}
|
||||||
|
PATTERN "${exclude_patterns}" EXCLUDE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endfunction()
|
|
@ -49,6 +49,8 @@ class MatlabWrapper(object):
|
||||||
}
|
}
|
||||||
"""Methods that should not be wrapped directly"""
|
"""Methods that should not be wrapped directly"""
|
||||||
whitelist = ['serializable', 'serialize']
|
whitelist = ['serializable', 'serialize']
|
||||||
|
"""Methods that should be ignored"""
|
||||||
|
ignore_methods = ['pickle']
|
||||||
"""Datatypes that do not need to be checked in methods"""
|
"""Datatypes that do not need to be checked in methods"""
|
||||||
not_check_type = []
|
not_check_type = []
|
||||||
"""Data types that are primitive types"""
|
"""Data types that are primitive types"""
|
||||||
|
@ -563,6 +565,8 @@ class MatlabWrapper(object):
|
||||||
for method in methods:
|
for method in methods:
|
||||||
if method.name in self.whitelist:
|
if method.name in self.whitelist:
|
||||||
continue
|
continue
|
||||||
|
if method.name in self.ignore_methods:
|
||||||
|
continue
|
||||||
|
|
||||||
comment += '%{name}({args})'.format(name=method.name, args=self._wrap_args(method.args))
|
comment += '%{name}({args})'.format(name=method.name, args=self._wrap_args(method.args))
|
||||||
|
|
||||||
|
@ -587,7 +591,7 @@ class MatlabWrapper(object):
|
||||||
file_name = self._wrapper_name() + '.cpp'
|
file_name = self._wrapper_name() + '.cpp'
|
||||||
|
|
||||||
wrapper_file = textwrap.dedent('''\
|
wrapper_file = textwrap.dedent('''\
|
||||||
# include <wrap/matlab.h>
|
# include <gtwrap/matlab.h>
|
||||||
# include <map>
|
# include <map>
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
@ -612,6 +616,9 @@ class MatlabWrapper(object):
|
||||||
methods = self._group_methods(methods)
|
methods = self._group_methods(methods)
|
||||||
|
|
||||||
for method in methods:
|
for method in methods:
|
||||||
|
if method in self.ignore_methods:
|
||||||
|
continue
|
||||||
|
|
||||||
if globals:
|
if globals:
|
||||||
self._debug("[wrap_methods] wrapping: {}..{}={}".format(method[0].parent.name, method[0].name,
|
self._debug("[wrap_methods] wrapping: {}..{}={}".format(method[0].parent.name, method[0].name,
|
||||||
type(method[0].parent.name)))
|
type(method[0].parent.name)))
|
||||||
|
@ -861,6 +868,8 @@ class MatlabWrapper(object):
|
||||||
method_name = method[0].name
|
method_name = method[0].name
|
||||||
if method_name in self.whitelist and method_name != 'serialize':
|
if method_name in self.whitelist and method_name != 'serialize':
|
||||||
continue
|
continue
|
||||||
|
if method_name in self.ignore_methods:
|
||||||
|
continue
|
||||||
|
|
||||||
if method_name == 'serialize':
|
if method_name == 'serialize':
|
||||||
serialize[0] = True
|
serialize[0] = True
|
||||||
|
@ -932,6 +941,9 @@ class MatlabWrapper(object):
|
||||||
format_name = list(static_method[0].name)
|
format_name = list(static_method[0].name)
|
||||||
format_name[0] = format_name[0].upper()
|
format_name[0] = format_name[0].upper()
|
||||||
|
|
||||||
|
if static_method[0].name in self.ignore_methods:
|
||||||
|
continue
|
||||||
|
|
||||||
method_text += textwrap.indent(textwrap.dedent('''\
|
method_text += textwrap.indent(textwrap.dedent('''\
|
||||||
function varargout = {name}(varargin)
|
function varargout = {name}(varargin)
|
||||||
'''.format(name=''.join(format_name))),
|
'''.format(name=''.join(format_name))),
|
||||||
|
@ -1464,7 +1476,7 @@ class MatlabWrapper(object):
|
||||||
"""Generate the c++ wrapper."""
|
"""Generate the c++ wrapper."""
|
||||||
# Includes
|
# Includes
|
||||||
wrapper_file = textwrap.dedent('''\
|
wrapper_file = textwrap.dedent('''\
|
||||||
#include <wrap/matlab.h>
|
#include <gtwrap/matlab.h>
|
||||||
#include <map>\n
|
#include <map>\n
|
||||||
#include <boost/archive/text_iarchive.hpp>
|
#include <boost/archive/text_iarchive.hpp>
|
||||||
#include <boost/archive/text_oarchive.hpp>
|
#include <boost/archive/text_oarchive.hpp>
|
||||||
|
@ -1473,7 +1485,16 @@ class MatlabWrapper(object):
|
||||||
|
|
||||||
includes_list = sorted(list(self.includes.keys()), key=lambda include: include.header)
|
includes_list = sorted(list(self.includes.keys()), key=lambda include: include.header)
|
||||||
|
|
||||||
wrapper_file += reduce(lambda x, y: str(x) + '\n' + str(y), includes_list) + '\n'
|
# Check the number of includes.
|
||||||
|
# If no includes, do nothing, if 1 then just append newline.
|
||||||
|
# if more than one, concatenate them with newlines.
|
||||||
|
if len(includes_list) == 0:
|
||||||
|
pass
|
||||||
|
elif len(includes_list) == 1:
|
||||||
|
wrapper_file += (str(includes_list[0]) + '\n')
|
||||||
|
else:
|
||||||
|
wrapper_file += reduce(lambda x, y: str(x) + '\n' + str(y), includes_list)
|
||||||
|
wrapper_file += '\n'
|
||||||
|
|
||||||
typedef_instances = '\n'
|
typedef_instances = '\n'
|
||||||
typedef_collectors = ''
|
typedef_collectors = ''
|
||||||
|
|
|
@ -76,6 +76,21 @@ class PybindWrapper(object):
|
||||||
gtsam::deserialize(serialized, *self);
|
gtsam::deserialize(serialized, *self);
|
||||||
}}, py::arg("serialized"))
|
}}, py::arg("serialized"))
|
||||||
'''.format(class_inst=cpp_class + '*'))
|
'''.format(class_inst=cpp_class + '*'))
|
||||||
|
if cpp_method == "pickle":
|
||||||
|
if not cpp_class in self._serializing_classes:
|
||||||
|
raise ValueError("Cannot pickle a class which is not serializable")
|
||||||
|
return textwrap.dedent('''
|
||||||
|
.def(py::pickle(
|
||||||
|
[](const {cpp_class} &a){{ // __getstate__
|
||||||
|
/* Returns a string that encodes the state of the object */
|
||||||
|
return py::make_tuple(gtsam::serialize(a));
|
||||||
|
}},
|
||||||
|
[](py::tuple t){{ // __setstate__
|
||||||
|
{cpp_class} obj;
|
||||||
|
gtsam::deserialize(t[0].cast<std::string>(), obj);
|
||||||
|
return obj;
|
||||||
|
}}))
|
||||||
|
'''.format(cpp_class=cpp_class))
|
||||||
|
|
||||||
is_method = isinstance(method, instantiator.InstantiatedMethod)
|
is_method = isinstance(method, instantiator.InstantiatedMethod)
|
||||||
is_static = isinstance(method, parser.StaticMethod)
|
is_static = isinstance(method, parser.StaticMethod)
|
||||||
|
@ -318,3 +333,4 @@ class PybindWrapper(object):
|
||||||
wrapped_namespace=wrapped_namespace,
|
wrapped_namespace=wrapped_namespace,
|
||||||
boost_class_export=boost_class_export,
|
boost_class_export=boost_class_export,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <wrap/matlab.h>
|
#include <gtwrap/matlab.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <boost/archive/text_iarchive.hpp>
|
#include <boost/archive/text_iarchive.hpp>
|
||||||
|
|
|
@ -47,6 +47,17 @@ PYBIND11_MODULE(geometry_py, m_) {
|
||||||
[](gtsam::Point2* self, string serialized){
|
[](gtsam::Point2* self, string serialized){
|
||||||
gtsam::deserialize(serialized, *self);
|
gtsam::deserialize(serialized, *self);
|
||||||
}, py::arg("serialized"))
|
}, py::arg("serialized"))
|
||||||
|
|
||||||
|
.def(py::pickle(
|
||||||
|
[](const gtsam::Point2 &a){ // __getstate__
|
||||||
|
/* Returns a string that encodes the state of the object */
|
||||||
|
return py::make_tuple(gtsam::serialize(a));
|
||||||
|
},
|
||||||
|
[](py::tuple t){ // __setstate__
|
||||||
|
gtsam::Point2 obj;
|
||||||
|
gtsam::deserialize(t[0].cast<std::string>(), obj);
|
||||||
|
return obj;
|
||||||
|
}))
|
||||||
;
|
;
|
||||||
|
|
||||||
py::class_<gtsam::Point3, std::shared_ptr<gtsam::Point3>>(m_gtsam, "Point3")
|
py::class_<gtsam::Point3, std::shared_ptr<gtsam::Point3>>(m_gtsam, "Point3")
|
||||||
|
@ -62,6 +73,17 @@ PYBIND11_MODULE(geometry_py, m_) {
|
||||||
gtsam::deserialize(serialized, *self);
|
gtsam::deserialize(serialized, *self);
|
||||||
}, py::arg("serialized"))
|
}, py::arg("serialized"))
|
||||||
|
|
||||||
|
.def(py::pickle(
|
||||||
|
[](const gtsam::Point3 &a){ // __getstate__
|
||||||
|
/* Returns a string that encodes the state of the object */
|
||||||
|
return py::make_tuple(gtsam::serialize(a));
|
||||||
|
},
|
||||||
|
[](py::tuple t){ // __setstate__
|
||||||
|
gtsam::Point3 obj;
|
||||||
|
gtsam::deserialize(t[0].cast<std::string>(), obj);
|
||||||
|
return obj;
|
||||||
|
}))
|
||||||
|
|
||||||
.def_static("staticFunction",[](){return gtsam::Point3::staticFunction();})
|
.def_static("staticFunction",[](){return gtsam::Point3::staticFunction();})
|
||||||
.def_static("StaticFunctionRet",[]( double z){return gtsam::Point3::StaticFunctionRet(z);}, py::arg("z"));
|
.def_static("StaticFunctionRet",[]( double z){return gtsam::Point3::StaticFunctionRet(z);}, py::arg("z"));
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ class Point2 {
|
||||||
VectorNotEigen vectorConfusion();
|
VectorNotEigen vectorConfusion();
|
||||||
|
|
||||||
void serializable() const; // Sets flag and creates export, but does not make serialization functions
|
void serializable() const; // Sets flag and creates export, but does not make serialization functions
|
||||||
|
|
||||||
|
// enable pickling in python
|
||||||
|
void pickle() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <gtsam/geometry/Point3.h>
|
#include <gtsam/geometry/Point3.h>
|
||||||
|
@ -35,6 +38,9 @@ class Point3 {
|
||||||
|
|
||||||
// enabling serialization functionality
|
// enabling serialization functionality
|
||||||
void serialize() const; // Just triggers a flag internally and removes actual function
|
void serialize() const; // Just triggers a flag internally and removes actual function
|
||||||
|
|
||||||
|
// enable pickling in python
|
||||||
|
void pickle() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue