Skip to content

Commit

Permalink
Merge pull request #746 from diegoferigo/refactor/matlab_bindings
Browse files Browse the repository at this point in the history
Refactor CMake project for Matlab bindings
  • Loading branch information
traversaro authored Oct 9, 2020
2 parents c41ec8a + a8e7e85 commit d8808b0
Showing 1 changed file with 133 additions and 95 deletions.
228 changes: 133 additions & 95 deletions bindings/matlab/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,38 +1,10 @@
# As Matlab support is higly experimental for now in swig, we split the binding generation in two phases:
# * Generation of the .cxx code, left to the author of the library that needs to have a recent (non-standard) swig
# As Matlab support is highly experimental for now in swig, we split the binding generation in two phases:
#
# * Generation of the .cxx code, left to the author of the library that needs to have a recent (non-standard) swig.
# * Compilation of the .cxx, that is left to the user that compiles the library.
# For doing this we split the traditional swig_add_module macro in two macro: one for generating the wrapper and one
# for compiling it. As soon as upstream swig distributed by the distro gains Matlab support, we can drop this workaround
macro(SWIG_GENERATE_MODULE name language)
SWIG_MODULE_INITIALIZE(${name} ${language})
set(swig_dot_i_sources)
set(swig_other_sources)
foreach(it ${ARGN})
if(${it} MATCHES ".*\\.i$")
set(swig_dot_i_sources ${swig_dot_i_sources} "${it}")
else()
set(swig_other_sources ${swig_other_sources} "${it}")
endif()
endforeach()

set(swig_generated_sources)
foreach(it ${swig_dot_i_sources})
SWIG_ADD_SOURCE_TO_MODULE(${name} swig_generated_source ${it})
set(swig_generated_sources ${swig_generated_sources} "${swig_generated_source}")
endforeach()
endmacro()

macro(SWIG_COMPILE_MODULE_MEX name language)
SWIG_MODULE_INITIALIZE(${name} ${language})
matlab_add_mex(NAME ${SWIG_MODULE_${name}_REAL_NAME}
MODULE
SRC ${swig_generated_sources}
${swig_other_sources})
# Avoid the "d" suffix for mex files as it prevents the bindings to call the mex file correctly
set_target_properties(${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES DEBUG_POSTFIX "")
endmacro()

# Options related to installation directories
#
# If you want to install bindings for packaging you may need to install
# following several rules (for example placing the .m files in share
# and the compiled libraries in some architecture-specific directory)
Expand All @@ -41,13 +13,19 @@ endmacro()
# the bindings for a developer that compiled the library from source:
# to use the octave bindings just add <prefix>/octave to the octave path,
# to use the matlab bindings just add <prefix>/mex to the matlab path.
set(IDYNTREE_INSTALL_MATLAB_LIBDIR "mex" CACHE STRING "Location (relative to the install prefix) in which the Matlab mex libraries are installed.")
set(IDYNTREE_INSTALL_MATLAB_LIBDIR "mex" CACHE
STRING "Location (relative to the install prefix) in which the Matlab mex libraries are installed.")
set(IDYNTREE_INSTALL_MATLAB_MFILESDIR "mex" CACHE
STRING "Location (relative to the install prefix) in which the Matlab .m files are installed.")
set(IDYNTREE_INSTALL_OCTAVE_LIBDIR "octave" CACHE
STRING "Location (relative to the install prefix) in which the Octave mex libraries are installed.")
set(IDYNTREE_INSTALL_OCTAVE_MFILESDIR "octave" CACHE
STRING "Location (relative to the install prefix) in which the Octave .m files are installed.")

# Mark the cached options as advanced
mark_as_advanced(IDYNTREE_INSTALL_MATLAB_LIBDIR)
set(IDYNTREE_INSTALL_MATLAB_MFILESDIR "mex" CACHE STRING "Location (relative to the install prefix) in which the Matlab .m files are installed.")
mark_as_advanced(IDYNTREE_INSTALL_MATLAB_MFILESDIR)
set(IDYNTREE_INSTALL_OCTAVE_LIBDIR "octave" CACHE STRING "Location (relative to the install prefix) in which the Octave mex libraries are installed.")
mark_as_advanced(IDYNTREE_INSTALL_OCTAVE_LIBDIR)
set(IDYNTREE_INSTALL_OCTAVE_MFILESDIR "octave" CACHE STRING "Location (relative to the install prefix) in which the Octave .m files are installed.")
mark_as_advanced(IDYNTREE_INSTALL_OCTAVE_MFILESDIR)

# The name of the generated source named is
Expand All @@ -58,7 +36,7 @@ set(sourcename iDynTreeMATLAB_wrap)
# The name of the generated source should instead match the one
# used by SWIG when generating the .m file.
# See https://github.com/jaeandersson/swig/issues/44 for more details
set(mexname iDynTreeMEX)
set(mexname iDynTreeMEX)

# Directory in which the bindings are generated
set(MEX_BINDINGS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/autogenerated)
Expand All @@ -67,97 +45,157 @@ set(MEX_BINDINGS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/autogenerated)
set(HIGH_LEVEL_WRAPPERS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/+iDynTreeWrappers)
set(MATLAB_WRAPPERS_BINDINGS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})

# Generate SWIG wrapper
if(IDYNTREE_GENERATE_MATLAB)
# generate the wrapper
set(swig_generated_sources)
set_source_files_properties(../iDynTree.i PROPERTIES CPLUSPLUS ON)
# generate files in the source directory, so we can commit it
set(CMAKE_SWIG_OUTDIR ${MEX_BINDINGS_SOURCE_DIR})
#set(CMAKE_SWIG_FLAGS "-redirectoutput")
set(SWIG_MODULE_${mexname}_EXTRA_DEPS ${IDYNTREE_SWIG_DEPENDS_I_FILES})
swig_generate_module(${mexname} matlab ../iDynTree.i)
set_source_files_properties(${MEX_BINDINGS_SOURCE_DIR}/${sourcename}.cxx PROPERTIES GENERATED 1)
endif()

# use previously generated files
set(swig_generated_sources ${MEX_BINDINGS_SOURCE_DIR}/${sourcename}.cxx)
set(swig_other_sources)

# Set the generated mex name to be iDynTreeMEX, as it is the default one used by SWIG while generating bindings
# Set the .i files to generate the bindings
set(iDynTree_SWIG_I_FILE ../iDynTree.i)
set(iDynTree_SWIG_I_OTHER
../ignore.i
../joints.i
../sensors.i
matlab.i
matlab_mat4x4vec.i
matlab_matvec.i
matlab_spatialvec.i)

# Generate SWIG wrapper
macro(GET_LIBRARY_FROM_SWIG_WRAPPER i_main_file i_other_files target_name generate outputdir)

set_property(SOURCE ${i_main_file} PROPERTY CPLUSPLUS ON)
set(CMAKE_SWIG_OUTDIR ${outputdir})

if(${generate} AND NOT bindings_have_been_generated)
find_package(SWIG)
set(bindings_have_been_generated TRUE)
swig_add_library(${target_name} LANGUAGE matlab SOURCES ${i_main_file})
# Add the dependency to other .i files
set_property(
TARGET ${target_name}
PROPERTY SWIG_DEPENDS ${i_other_files})
else()
add_library(${target_name} MODULE ${swig_generated_sources})
set_target_properties(${target_name} PROPERTIES DEBUG_POSTFIX "")
set_source_files_properties(${swig_generated_sources} PROPERTIES GENERATED 1)
endif()

endmacro()

# ==============
# COMPILE MATLAB
# ==============

if(IDYNTREE_USES_MATLAB)
find_package(Matlab
REQUIRED
MX_LIBRARY
MAIN_PROGRAM)

swig_compile_module_mex(${mexname} matlab)
swig_link_libraries(${mexname} ${Matlab_LIBRARIES} ${IDYNTREE_LIBRARIES} idyntree-core)
target_include_directories(${mexname} PUBLIC ${Matlab_INCLUDE_DIRS})
set(target_name ${mexname})

get_library_from_swig_wrapper(
${iDynTree_SWIG_I_FILE}
"${iDynTree_SWIG_I_OTHER}"
${target_name}
${IDYNTREE_GENERATE_MATLAB}
${MEX_BINDINGS_SOURCE_DIR})

set_target_properties(${target_name}
PROPERTIES OUTPUT_NAME ${mexname}
PREFIX ""
SUFFIX .mex)

target_link_libraries(${target_name} ${IDYNTREE_LIBRARIES} idyntree-core)
target_include_directories(${target_name} PUBLIC ${Matlab_INCLUDE_DIRS})
set_target_properties(${target_name} PROPERTIES PREFIX "" SUFFIX .${Matlab_MEX_EXTENSION})

set_target_properties(${mexname} PROPERTIES PREFIX "" SUFFIX .${Matlab_MEX_EXTENSION})
# entry point in the mex file + taking care of visibility and symbol clashes.
if(WIN32)
set_target_properties(${mexname}
set_target_properties(${target_name}
PROPERTIES
DEFINE_SYMBOL "DLL_EXPORT_SYM=__declspec(dllexport)")
endif()

# Install the generated front-end to ${CMAKE_INSTALL_PREFIX}/mex
install(DIRECTORY ${MEX_BINDINGS_SOURCE_DIR}/+iDynTree DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(FILES ${MEX_BINDINGS_SOURCE_DIR}/SwigGet.m DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(FILES ${MEX_BINDINGS_SOURCE_DIR}/SwigRef.m DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(FILES ${MEX_BINDINGS_SOURCE_DIR}/SwigMem.m DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(TARGETS ${mexname} DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_LIBDIR})
install(
DIRECTORY ${MEX_BINDINGS_SOURCE_DIR}/+iDynTree
DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(
FILES
${MEX_BINDINGS_SOURCE_DIR}/SwigGet.m
${MEX_BINDINGS_SOURCE_DIR}/SwigRef.m
${MEX_BINDINGS_SOURCE_DIR}/SwigMem.m
DESTINATION
${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(
TARGETS ${target_name}
DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_LIBDIR})

# Install the high-level-matlab-wrappers
install(DIRECTORY ${HIGH_LEVEL_WRAPPERS_SOURCE_DIR} DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(
DIRECTORY ${HIGH_LEVEL_WRAPPERS_SOURCE_DIR}
DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})

# Install the matlab-visualization-external-functions
install(FILES ${IDYNTREE_INTERNAL_MESH2TRI_PATH}/mesh2tri.m DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})

#On new versions of Clang, MATLAB requires C++11.
#I enable it on all Clangs
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if (${CMAKE_GENERATOR} MATCHES "Xcode")
#this should set explictly the option in xcode. Unfortunately it does not work.
set(XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "C++11")
endif(${CMAKE_GENERATOR} MATCHES "Xcode")
endif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
install(
FILES ${IDYNTREE_INTERNAL_MESH2TRI_PATH}/mesh2tri.m
DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})

endif()

# ==============
# COMPILE OCTAVE
# ==============

if(IDYNTREE_USES_OCTAVE)
find_package(Octave REQUIRED)
if ("${OCTAVE_VERSION_STRING}" VERSION_LESS 4.0)
message(FATAL_ERROR "Octave mex-based bindings required at least Octave version 4.0 or greater.")
endif ()

# Compile MEX file
add_library(idyntreeOctaveMex MODULE ${swig_generated_sources} ${swig_other_sources})
target_include_directories(idyntreeOctaveMex PUBLIC ${OCTAVE_INCLUDE_DIRS})
find_package(Octave 4.0 REQUIRED)

set(target_name idyntreeOctaveMex)

# Get the target
get_library_from_swig_wrapper(
${iDynTree_SWIG_I_FILE}
"${iDynTree_SWIG_I_OTHER}"
${target_name}
${IDYNTREE_GENERATE_MATLAB}
${MEX_BINDINGS_SOURCE_DIR})

target_include_directories(${target_name} PUBLIC ${OCTAVE_INCLUDE_DIRS})

if(APPLE)
target_link_libraries(idyntreeOctaveMex ${iDynTree_LIBRARIES})
set_target_properties(idyntreeOctaveMex PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
target_link_libraries(${target_name} ${iDynTree_LIBRARIES})
set_target_properties(${target_name} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
else()
target_link_libraries(idyntreeOctaveMex ${OCTAVE_LIBRARIES} ${iDynTree_LIBRARIES})
target_link_libraries(${target_name} ${OCTAVE_LIBRARIES} ${iDynTree_LIBRARIES})
endif()
set_target_properties(idyntreeOctaveMex
PROPERTIES OUTPUT_NAME "iDynTreeMEX"

set_target_properties(${target_name}
PROPERTIES OUTPUT_NAME ${mexname}
PREFIX ""
SUFFIX .mex)
install(TARGETS idyntreeOctaveMex DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_LIBDIR})

# Install the generated front-end to ${CMAKE_INSTALL_PREFIX}/mex
install(DIRECTORY ${MEX_BINDINGS_SOURCE_DIR}/+iDynTree DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_MFILESDIR})
install(FILES ${MEX_BINDINGS_SOURCE_DIR}/SwigGet.m DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_MFILESDIR})
install(FILES ${MEX_BINDINGS_SOURCE_DIR}/SwigRef.m DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_MFILESDIR})
install(FILES ${MEX_BINDINGS_SOURCE_DIR}/SwigMem.m DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_MFILESDIR})
# Install the target
install(
TARGETS ${target_name}
DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_LIBDIR})

# Install the generated front-end to ${CMAKE_INSTALL_PREFIX}/octave
install(
DIRECTORY ${MEX_BINDINGS_SOURCE_DIR}/+iDynTree
DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_MFILESDIR})
install(
FILES
${MEX_BINDINGS_SOURCE_DIR}/SwigGet.m
${MEX_BINDINGS_SOURCE_DIR}/SwigRef.m
${MEX_BINDINGS_SOURCE_DIR}/SwigMem.m
DESTINATION
${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_OCTAVE_MFILESDIR})

# Install the high-level-matlab-wrappers
install(DIRECTORY ${HIGH_LEVEL_WRAPPERS_SOURCE_DIR} DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
install(
DIRECTORY ${HIGH_LEVEL_WRAPPERS_SOURCE_DIR}
DESTINATION ${CMAKE_INSTALL_PREFIX}/${IDYNTREE_INSTALL_MATLAB_MFILESDIR})
endif()

# if compile tests compile also matlab/octave tests
Expand Down

0 comments on commit d8808b0

Please sign in to comment.