# Set up cache options option(GTSAM_MEX_BUILD_STATIC_MODULE "Build MATLAB wrapper statically (increases build time)" OFF) set(GTSAM_BUILD_MEX_BINARY_FLAGS "" CACHE STRING "Extra flags for running Matlab MEX compilation") set(GTSAM_TOOLBOX_INSTALL_PATH "" CACHE PATH "Matlab toolbox destination, blank defaults to CMAKE_INSTALL_PREFIX/gtsam_toolbox") if(NOT GTSAM_TOOLBOX_INSTALL_PATH) set(GTSAM_TOOLBOX_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}/gtsam_toolbox") endif() # GTSAM_MEX_BUILD_STATIC_MODULE is not for Windows - on Windows any static # are already compiled into the library by the linker if(GTSAM_MEX_BUILD_STATIC_MODULE AND WIN32) message(FATAL_ERROR "GTSAM_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() # Try to automatically configure mex path if(APPLE) file(GLOB matlab_bin_directories "/Applications/MATLAB*/bin") set(mex_program_name "mex") elseif(WIN32) file(GLOB matlab_bin_directories "C:/Program Files*/MATLAB/*/bin") set(mex_program_name "mex.bat") else() file(GLOB matlab_bin_directories "/usr/local/MATLAB/*/bin") 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. list(REVERSE matlab_bin_directories) # Reverse list so the highest version (sorted alphabetically) is preferred find_program(mex_command ${mex_program_name} PATHS ${matlab_bin_directories} 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) get_filename_component(MATLAB_ROOT "${mex_path}/.." ABSOLUTE) set(MATLAB_ROOT "${MATLAB_ROOT}" CACHE PATH "Path to MATLAB installation root (e.g. /usr/local/MATLAB/R2012a)") # User-friendly wrapping function. Builds a mex module from the provided # interfaceHeader. For example, for the interface header /path/to/gtsam.h, # this will build the wrap module 'gtsam'. # Params: # interfaceHeader : Absolute or relative path to the interface definition file # linkLibraries : All dependent CMake target names, library names, or full library paths # extraIncludeDirs : Extra include directories, in addition to those already passed to include_directories(...) # extraMexFlags : Any additional compiler flags function(wrap_and_install_library interfaceHeader linkLibraries extraIncludeDirs extraMexFlags) wrap_library_internal("${interfaceHeader}" "${linkLibraries}" "${extraIncludeDirs}" "${mexFlags}") install_wrapped_library_internal("${interfaceHeader}") endfunction() 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 "${PROJECT_BINARY_DIR}/wrap/${moduleName}/${moduleName}_wrapper.cpp") set(compiled_mex_modules_root "${PROJECT_BINARY_DIR}/wrap/${moduleName}_mex") message(STATUS "Building wrap module ${moduleName}") # Find matlab.h in GTSAM if("${PROJECT_NAME}" STREQUAL "GTSAM") set(matlab_h_path "${PROJECT_SOURCE_DIR}") else() if(NOT GTSAM_INCLUDE_DIR) message(FATAL_ERROR "You must call find_package(GTSAM) before using wrap") endif() list(GET GTSAM_INCLUDE_DIR 0 installed_includes_path) set(matlab_h_path "${installed_includes_path}/wrap") endif() # Add -shared or -static suffix to targets set(correctedOtherLibraries "") set(otherLibraryTargets "") foreach(lib ${moduleName} ${linkLibraries}) if(TARGET ${lib}) list(APPEND correctedOtherLibraries ${lib}) list(APPEND otherLibraryTargets ${lib}) elseif(TARGET ${lib}-shared) # Prefer the shared library if we have both shared and static) list(APPEND correctedOtherLibraries ${lib}-shared) list(APPEND otherLibraryTargets ${lib}-shared) elseif(TARGET ${lib}-static) list(APPEND correctedOtherLibraries ${lib}-static) list(APPEND otherLibraryTargets ${lib}-static) else() list(APPEND correctedOtherLibraries ${lib}) endif() endforeach() # Set up generation of module source file file(MAKE_DIRECTORY "${generated_files_path}") add_custom_command( OUTPUT ${generated_cpp_file} DEPENDS ${interfaceHeader} wrap ${module_library_target} ${otherLibraryTargets} COMMAND wrap ${modulePath} ${moduleName} ${generated_files_path} ${matlab_h_path} VERBATIM WORKING_DIRECTORY ${generated_files_path}) # Set up building of mex module string(REPLACE ";" " " extraMexFlagsSpaced "${extraMexFlags}") string(REPLACE ";" " " mexFlagsSpaced "${GTSAM_BUILD_MEX_BINARY_FLAGS}") add_library(${moduleName}_wrapper MODULE ${generated_cpp_file} ${interfaceHeader}) target_link_libraries(${moduleName}_wrapper ${correctedOtherLibraries}) set_target_properties(${moduleName}_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}_wrapper APPEND_STRING PROPERTY COMPILE_FLAGS " ${extraMexFlagsSpaced} ${mexFlagsSpaced} \"-I${MATLAB_ROOT}/extern/include\" -DMATLAB_MEX_FILE -DMX_COMPAT_32") set_property(TARGET ${moduleName}_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}_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}_wrapper "${mxLibPath}/libmex.lib" "${mxLibPath}/libmx.lib" "${mxLibPath}/libmat.lib") set_target_properties(${moduleName}_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}_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}_distclean COMMAND cmake -E remove_directory ${generated_files_path} COMMAND cmake -E remove_directory ${compiled_mex_modules_root}) endfunction() 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 ${GTSAM_TOOLBOX_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_TOOLBOX_INSTALL_PATH if there is one get_filename_component(location "${GTSAM_TOOLBOX_INSTALL_PATH}" PATH) get_filename_component(name "${GTSAM_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}_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 ${GTSAM_TOOLBOX_INSTALL_PATH} FILES_MATCHING PATTERN "*.m") install(TARGETS ${moduleName}_wrapper LIBRARY DESTINATION ${GTSAM_TOOLBOX_INSTALL_PATH} RUNTIME DESTINATION ${GTSAM_TOOLBOX_INSTALL_PATH}) endif() endfunction() # Function to setup codegen and building of the wrap toolbox # # params: # moduleName : the name of the module, interface file must be called moduleName.h # mexFlags : Compilation flags to be passed to the mex compiler # modulePath : relative path to module markup header file (called moduleName.h) # otherLibraries : list of library targets this should depend on # toolboxPath : the directory in which to generate/build wrappers # wrap_header_path : path to the installed wrap header function(wrap_library_generic moduleName mexFlags modulePath otherLibraries toolbox_path wrap_header_path) if(NOT "${CMAKE_PROJECT_NAME}" STREQUAL "GTSAM") message("Your project uses wrap_library or wrap_library_generic - this is deprecated, please use the more user-friendly function wrap_and_install_library") endif() # Append module name to link libraries to keep original behavior list(APPEND otherLibraries ${moduleName}) # Set up arguments set(interfaceHeader ${modulePath}/${moduleName}.h) # Call internal function wrap_library_internal("${interfaceHeader}" "${otherLibraries}" "" "${mexFlags}") endfunction(wrap_library_generic) # Function to setup codegen, building and installation of the wrap toolbox # This wrap setup function assumes that the toolbox will be installed directly, # with predictable matlab.h sourcing. Use this version when the toolbox will be used # from the installed version, rather than in place. # Assumes variable GTSAM_WRAP_HEADER_PATH has been set # params: # moduleName : the name of the module, interface file must be called moduleName.h # mexFlags : Compilation flags to be passed to the mex compiler # modulePath : relative path to module markup header file (called moduleName.h) # otherLibraries : list of library targets this should depend on function(wrap_library moduleName mexFlags modulePath otherLibraries) # Toolbox generation path goes in build folder set(toolbox_base_path ${PROJECT_BINARY_DIR}/wrap) set(toolbox_path ${toolbox_base_path}/${moduleName}) # Call generic version of function wrap_library_generic("${moduleName}" "${mexFlags}" "${modulePath}" "${otherLibraries}" "${toolbox_path}" "${GTSAM_WRAP_HEADER_PATH}") install_wrapped_library_internal("${modulePath}/${moduleName}.h") endfunction(wrap_library) # 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 "") 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_TOOLBOX_INSTALL_PATH if there is one get_filename_component(location "${GTSAM_TOOLBOX_INSTALL_PATH}" PATH) get_filename_component(name "${GTSAM_TOOLBOX_INSTALL_PATH}" NAME) install(DIRECTORY "${source_directory}" DESTINATION "${location}/${name}${build_type_tag}" CONFIGURATIONS "${build_type}" FILES_MATCHING ${patterns_args} PATTERN ".svn" EXCLUDE) endforeach() else() install(DIRECTORY "${source_directory}" DESTINATION "${GTSAM_TOOLBOX_INSTALL_PATH}" FILES_MATCHING ${patterns_args} PATTERN ".svn" EXCLUDE) endif() endfunction()