244 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			CMake
		
	
	
		
		
			
		
	
	
			244 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			CMake
		
	
	
| 
								 | 
							
								if(GTWRAP_PYTHON_PACKAGE_DIR)
							 | 
						||
| 
								 | 
							
								  # packaged
							 | 
						||
| 
								 | 
							
								  set(GTWRAP_PACKAGE_DIR "${GTWRAP_PYTHON_PACKAGE_DIR}")
							 | 
						||
| 
								 | 
							
								else()
							 | 
						||
| 
								 | 
							
								  set(GTWRAP_PACKAGE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
							 | 
						||
| 
								 | 
							
								endif()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Get the Python version
							 | 
						||
| 
								 | 
							
								include(GtwrapUtils)
							 | 
						||
| 
								 | 
							
								message(STATUS "Checking Python Version")
							 | 
						||
| 
								 | 
							
								gtwrap_get_python_version(${WRAP_PYTHON_VERSION})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								message(STATUS "Setting Python version for wrapper")
							 | 
						||
| 
								 | 
							
								set(PYBIND11_PYTHON_VERSION ${WRAP_PYTHON_VERSION})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# User-friendly Pybind11 wrapping and installing function. Builds a Pybind11
							 | 
						||
| 
								 | 
							
								# module from the provided interface_headers. For example, for the interface
							 | 
						||
| 
								 | 
							
								# header gtsam.h, this will build the wrap module 'gtsam_py.cc'.
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Arguments:
							 | 
						||
| 
								 | 
							
								# ~~~
							 | 
						||
| 
								 | 
							
								# target: The Make target
							 | 
						||
| 
								 | 
							
								# interface_headers:  List of paths to the wrapper interface definition files. The top level interface file should be first.
							 | 
						||
| 
								 | 
							
								# generated_cpp: The name of the cpp file which is generated from the tpl file.
							 | 
						||
| 
								 | 
							
								# module_name: The name of the Python module to use.
							 | 
						||
| 
								 | 
							
								# top_namespace: The C++ namespace under which the code to be wrapped exists.
							 | 
						||
| 
								 | 
							
								# ignore_classes: CMake list of classes to ignore from wrapping.
							 | 
						||
| 
								 | 
							
								# install_path: Destination to install the library.
							 | 
						||
| 
								 | 
							
								# module_template: The template file (.tpl) from which to generate the Pybind11 module.
							 | 
						||
| 
								 | 
							
								# libs: Libraries to link with.
							 | 
						||
| 
								 | 
							
								# dependencies: Dependencies which need to be built before the wrapper.
							 | 
						||
| 
								 | 
							
								# use_boost (optional): Flag indicating whether to include Boost.
							 | 
						||
| 
								 | 
							
								function(
							 | 
						||
| 
								 | 
							
								  pybind_wrap
							 | 
						||
| 
								 | 
							
								  target
							 | 
						||
| 
								 | 
							
								  interface_headers
							 | 
						||
| 
								 | 
							
								  generated_cpp
							 | 
						||
| 
								 | 
							
								  module_name
							 | 
						||
| 
								 | 
							
								  top_namespace
							 | 
						||
| 
								 | 
							
								  ignore_classes
							 | 
						||
| 
								 | 
							
								  module_template
							 | 
						||
| 
								 | 
							
								  libs
							 | 
						||
| 
								 | 
							
								  dependencies)
							 | 
						||
| 
								 | 
							
								  set(ExtraMacroArgs ${ARGN})
							 | 
						||
| 
								 | 
							
								  list(GET ExtraMacroArgs 0 USE_BOOST)
							 | 
						||
| 
								 | 
							
								  if(USE_BOOST)
							 | 
						||
| 
								 | 
							
								    set(_WRAP_BOOST_ARG "--use-boost")
							 | 
						||
| 
								 | 
							
								  else(USE_BOOST)
							 | 
						||
| 
								 | 
							
								    set(_WRAP_BOOST_ARG "")
							 | 
						||
| 
								 | 
							
								  endif(USE_BOOST)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(UNIX)
							 | 
						||
| 
								 | 
							
								    set(GTWRAP_PATH_SEPARATOR ":")
							 | 
						||
| 
								 | 
							
								  else()
							 | 
						||
| 
								 | 
							
								    set(GTWRAP_PATH_SEPARATOR ";")
							 | 
						||
| 
								 | 
							
								  endif()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Create a copy of interface_headers so we can freely manipulate it
							 | 
						||
| 
								 | 
							
								  set(interface_files ${interface_headers})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Pop the main interface file so that interface_files has only submodules.
							 | 
						||
| 
								 | 
							
								  list(POP_FRONT interface_files main_interface)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Convert .i file names to .cpp file names.
							 | 
						||
| 
								 | 
							
								  foreach(interface_file ${interface_files})
							 | 
						||
| 
								 | 
							
								    # This block gets the interface file name and does the replacement
							 | 
						||
| 
								 | 
							
								    get_filename_component(interface ${interface_file} NAME_WLE)
							 | 
						||
| 
								 | 
							
								    set(cpp_file "${interface}.cpp")
							 | 
						||
| 
								 | 
							
								    list(APPEND cpp_files ${cpp_file})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Wrap the specific interface header
							 | 
						||
| 
								 | 
							
								    # This is done so that we can create CMake dependencies in such a way so that when changing a single .i file,
							 | 
						||
| 
								 | 
							
								    # the others don't need to be regenerated.
							 | 
						||
| 
								 | 
							
								    # NOTE: We have to use `add_custom_command` so set the dependencies correctly.
							 | 
						||
| 
								 | 
							
								    # https://stackoverflow.com/questions/40032593/cmake-does-not-rebuild-dependent-after-prerequisite-changes
							 | 
						||
| 
								 | 
							
								    add_custom_command(
							 | 
						||
| 
								 | 
							
								      OUTPUT ${cpp_file}
							 | 
						||
| 
								 | 
							
								      COMMAND
							 | 
						||
| 
								 | 
							
								        ${CMAKE_COMMAND} -E env
							 | 
						||
| 
								 | 
							
								        "PYTHONPATH=${GTWRAP_PACKAGE_DIR}${GTWRAP_PATH_SEPARATOR}$ENV{PYTHONPATH}"
							 | 
						||
| 
								 | 
							
								        ${PYTHON_EXECUTABLE} ${PYBIND_WRAP_SCRIPT} --src "${interface_file}"
							 | 
						||
| 
								 | 
							
								          --out "${cpp_file}"  --module_name ${module_name}
							 | 
						||
| 
								 | 
							
								          --top_module_namespaces "${top_namespace}" --ignore ${ignore_classes}
							 | 
						||
| 
								 | 
							
								          --template ${module_template} --is_submodule ${_WRAP_BOOST_ARG}
							 | 
						||
| 
								 | 
							
								      DEPENDS "${interface_file}" ${module_template} "${module_name}/specializations/${interface}.h" "${module_name}/preamble/${interface}.h"
							 | 
						||
| 
								 | 
							
								      VERBATIM)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  endforeach()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  get_filename_component(main_interface_name ${main_interface} NAME_WLE)
							 | 
						||
| 
								 | 
							
								  set(main_cpp_file "${main_interface_name}.cpp")
							 | 
						||
| 
								 | 
							
								  list(PREPEND cpp_files ${main_cpp_file})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  add_custom_command(
							 | 
						||
| 
								 | 
							
								    OUTPUT ${main_cpp_file}
							 | 
						||
| 
								 | 
							
								    COMMAND
							 | 
						||
| 
								 | 
							
								      ${CMAKE_COMMAND} -E env
							 | 
						||
| 
								 | 
							
								      "PYTHONPATH=${GTWRAP_PACKAGE_DIR}${GTWRAP_PATH_SEPARATOR}$ENV{PYTHONPATH}"
							 | 
						||
| 
								 | 
							
								      ${PYTHON_EXECUTABLE} ${PYBIND_WRAP_SCRIPT} --src "${interface_headers}"
							 | 
						||
| 
								 | 
							
								      --out "${generated_cpp}" --module_name ${module_name}
							 | 
						||
| 
								 | 
							
								      --top_module_namespaces "${top_namespace}" --ignore ${ignore_classes}
							 | 
						||
| 
								 | 
							
								      --template ${module_template} ${_WRAP_BOOST_ARG}
							 | 
						||
| 
								 | 
							
								    DEPENDS "${main_interface}" ${module_template} "${module_name}/specializations/${main_interface_name}.h" "${module_name}/specializations/${main_interface_name}.h"
							 | 
						||
| 
								 | 
							
								    VERBATIM)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    add_custom_target(pybind_wrap_${module_name} DEPENDS ${cpp_files})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  pybind11_add_module(${target} "${cpp_files}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(APPLE)
							 | 
						||
| 
								 | 
							
								    # `type_info` objects will become "weak private external" if the templated
							 | 
						||
| 
								 | 
							
								    # class is initialized implicitly even if we explicitly export them with
							 | 
						||
| 
								 | 
							
								    # `WRAP_EXPORT`. If that happens, the `type_info` for the same templated
							 | 
						||
| 
								 | 
							
								    # class will diverge between shared libraries, causing `dynamic_cast` to
							 | 
						||
| 
								 | 
							
								    # fail. This is mitigated by telling Clang to mimic the MSVC behavior. See
							 | 
						||
| 
								 | 
							
								    # https://developer.apple.com/library/archive/technotes/tn2185/_index.html#//apple_ref/doc/uid/DTS10004200-CH1-SUBSECTION2
							 | 
						||
| 
								 | 
							
								    # https://github.com/CppMicroServices/CppMicroServices/pull/82/files
							 | 
						||
| 
								 | 
							
								    # https://www.russellmcc.com/posts/2013-08-03-rtti.html
							 | 
						||
| 
								 | 
							
								    target_compile_options(${target} PRIVATE "-fvisibility-ms-compat")
							 | 
						||
| 
								 | 
							
								  endif()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  add_dependencies(${target} pybind_wrap_${module_name})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(NOT "${libs}" STREQUAL "")
							 | 
						||
| 
								 | 
							
								    target_link_libraries(${target} PRIVATE "${libs}")
							 | 
						||
| 
								 | 
							
								  endif()
							 | 
						||
| 
								 | 
							
								  if(NOT "${dependencies}" STREQUAL "")
							 | 
						||
| 
								 | 
							
								    add_dependencies(${target} ${dependencies})
							 | 
						||
| 
								 | 
							
								  endif()
							 | 
						||
| 
								 | 
							
								endfunction()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Helper function to install python scripts and handle multiple build types
							 | 
						||
| 
								 | 
							
								# where the scripts should be installed to all build type toolboxes
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Arguments:
							 | 
						||
| 
								 | 
							
								# ~~~
							 | 
						||
| 
								 | 
							
								# source_directory: The source directory to be installed. "The last component
							 | 
						||
| 
								 | 
							
								#     of each directory name is appended to the destination directory but a
							 | 
						||
| 
								 | 
							
								#     trailing slash may be used to avoid this because it leaves the last
							 | 
						||
| 
								 | 
							
								#     component empty."
							 | 
						||
| 
								 | 
							
								#     (https://cmake.org/cmake/help/v3.3/command/install.html?highlight=install#installing-directories)
							 | 
						||
| 
								 | 
							
								# dest_directory: The destination directory to install to.
							 | 
						||
| 
								 | 
							
								# patterns: list of file patterns to install
							 | 
						||
| 
								 | 
							
								# ~~~
							 | 
						||
| 
								 | 
							
								function(install_python_scripts source_directory dest_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 "")
							 | 
						||
| 
								 | 
							
								      endif()
							 | 
						||
| 
								 | 
							
								      # Split up filename to strip trailing '/' in GTSAM_PY_INSTALL_PATH if
							 | 
						||
| 
								 | 
							
								      # there is one
							 | 
						||
| 
								 | 
							
								      get_filename_component(location "${dest_directory}" PATH)
							 | 
						||
| 
								 | 
							
								      get_filename_component(name "${dest_directory}" 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 "${dest_directory}"
							 | 
						||
| 
								 | 
							
								      FILES_MATCHING ${patterns_args}
							 | 
						||
| 
								 | 
							
								      PATTERN "${exclude_patterns}" EXCLUDE)
							 | 
						||
| 
								 | 
							
								  endif()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								endfunction()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Helper function to install specific files and handle multiple build types
							 | 
						||
| 
								 | 
							
								# where the scripts should be installed to all build type toolboxes
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Arguments:
							 | 
						||
| 
								 | 
							
								# ~~~
							 | 
						||
| 
								 | 
							
								# source_files: The source files to be installed.
							 | 
						||
| 
								 | 
							
								# dest_directory: The destination directory to install to.
							 | 
						||
| 
								 | 
							
								function(install_python_files source_files dest_directory)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(WRAP_BUILD_TYPE_POSTFIXES)
							 | 
						||
| 
								 | 
							
								    foreach(build_type ${CMAKE_CONFIGURATION_TYPES})
							 | 
						||
| 
								 | 
							
								      string(TOUPPER "${build_type}" build_type_upper)
							 | 
						||
| 
								 | 
							
								      set(build_type_tag "")
							 | 
						||
| 
								 | 
							
								      # Split up filename to strip trailing '/' in WRAP_PY_INSTALL_PATH if there
							 | 
						||
| 
								 | 
							
								      # is one
							 | 
						||
| 
								 | 
							
								      get_filename_component(location "${dest_directory}" PATH)
							 | 
						||
| 
								 | 
							
								      get_filename_component(name "${dest_directory}" NAME)
							 | 
						||
| 
								 | 
							
								      install(
							 | 
						||
| 
								 | 
							
								        FILES "${source_files}"
							 | 
						||
| 
								 | 
							
								        DESTINATION "${location}/${name}${build_type_tag}"
							 | 
						||
| 
								 | 
							
								        CONFIGURATIONS "${build_type}")
							 | 
						||
| 
								 | 
							
								    endforeach()
							 | 
						||
| 
								 | 
							
								  else()
							 | 
						||
| 
								 | 
							
								    install(FILES "${source_files}" DESTINATION "${dest_directory}")
							 | 
						||
| 
								 | 
							
								  endif()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								endfunction()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# ~~~
							 | 
						||
| 
								 | 
							
								# Copy over the directory from source_folder to dest_foler
							 | 
						||
| 
								 | 
							
								# ~~~
							 | 
						||
| 
								 | 
							
								function(copy_directory source_folder dest_folder)
							 | 
						||
| 
								 | 
							
								  if(${source_folder} STREQUAL ${dest_folder})
							 | 
						||
| 
								 | 
							
								    return()
							 | 
						||
| 
								 | 
							
								  endif()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  file(
							 | 
						||
| 
								 | 
							
								    GLOB files
							 | 
						||
| 
								 | 
							
								    LIST_DIRECTORIES true
							 | 
						||
| 
								 | 
							
								    RELATIVE "${source_folder}"
							 | 
						||
| 
								 | 
							
								    "${source_folder}/*")
							 | 
						||
| 
								 | 
							
								  foreach(path_file ${files})
							 | 
						||
| 
								 | 
							
								    get_filename_component(folder ${path_file} PATH)
							 | 
						||
| 
								 | 
							
								    get_filename_component(ext ${path_file} EXT)
							 | 
						||
| 
								 | 
							
								    set(ignored_ext ".tpl" ".h")
							 | 
						||
| 
								 | 
							
								    list(FIND ignored_ext "${ext}" _index)
							 | 
						||
| 
								 | 
							
								    if(${_index} GREATER -1)
							 | 
						||
| 
								 | 
							
								      continue()
							 | 
						||
| 
								 | 
							
								    endif()
							 | 
						||
| 
								 | 
							
								    # Create REAL folder
							 | 
						||
| 
								 | 
							
								    file(MAKE_DIRECTORY "${dest_folder}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Delete if it exists
							 | 
						||
| 
								 | 
							
								    file(REMOVE "${dest_folder}/${path_file}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Get OS dependent path to use in copy
							 | 
						||
| 
								 | 
							
								    file(TO_NATIVE_PATH "${source_folder}/${path_file}" target)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    file(COPY ${target} DESTINATION ${dest_folder})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  endforeach(path_file)
							 | 
						||
| 
								 | 
							
								endfunction(copy_directory)
							 |