Skip to content
Snippets Groups Projects
FindPythonModule.cmake 4.78 KiB
Newer Older
#------------------------------------------------------------------------------#
# Distributed under the OSI-approved Apache License, Version 2.0.  See
# accompanying file Copyright.txt for details.
#------------------------------------------------------------------------------#
#
# PythonModule
# -----------
#
# Try to find the path to a python module
#
# This module defines the following variables:
#
#   PythonModule_${module_NAME}_FOUND - System has the module in question
#
# and the following imported targets:
#   PythonModule::${module_NAME}
#
# This is intented to be called by specifying components for the module name
# and any additional header files and libraries that may be needed to interface
# with it.
#
# For example, say you want to search for mpi4py but you also need it's header
# files to use for interfacing with your own python bibndings that use MPI.
# You would call:
#
# find_package(PythonModule REQUIRED COMPONENTS mpi4py mpi4py/mpi4py.h)
#
# If found, this will generate an interface target with the appropriate usage
# requirements.
#
function(__get_filename_label fname var)
  get_filename_component(fname_dir "${fname}" DIRECTORY)
  get_filename_component(fname_nam "${fname}" NAME_WE)
  set(fname_label "${fname_dir}_${fname_nam}")
  string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" fname_label "${fname_label}")
  set(${var} ${fname_label} PARENT_SCOPE)
endfunction()

# Parse out the components
set(module_INCLUDES)
set(module_LIBRARIES)
foreach(comp IN LISTS PythonModule_FIND_COMPONENTS)
  if(comp MATCHES "^[a-zA-Z0-9_]*$")
    set(module_NAME ${comp})
  elseif(comp MATCHES "\\.h$")
    list(APPEND module_INCLUDES "${comp}")
  elseif(comp MATCHES "\\${CMAKE_SHARED_LIBRARY_SUFFIX}")
    list(APPEND module_LIBRARIES "${comp}")
  else()
    message(FATAL_ERROR "${comp}: Unknown python module component")
  endif()
endforeach()
if(NOT module_NAME)
  message(FATAL_ERROR "Unspecified Python module name")
endif()

if(NOT PythonModule_${module_NAME}_FOUND)
  set(PythonModule_${module_NAME}_PATH "" CACHE PATH "Python module ${module_NAME}")
  mark_as_advanced(PythonModule_${module_NAME}_PATH)

  # Try to handle the cross compiling case
  if(NOT PythonModule_${module_NAME}_PATH)
    if(CMAKE_CROSS_COMPILING)
      message(ERROR "find_python_module() invoked in cross-compiling mode, please set the following cache variables appropriately:")
      message("   PythonModule_${module_NAME}_PATH (advanced)")
      message("For details see ${CMAKE_BINARY_DIR}/TryRunResults.cmake")
      file(APPEND "${CMAKE_BINARY_DIR}/TryRunResults.cmake" "
set( PythonModule_${module_NAME}_PATH
  \"PLEASE_FILL_OUT\"
  CACHE PATH \"Python module ${module_NAME}\" FORCE)
")
    else()
      include(CMakeFindDependencyMacro)
      find_dependency(PythonInterp)
      if(PythonInterp_FOUND)
        execute_process(
          COMMAND
            ${PYTHON_EXECUTABLE}
              -c "import ${module_NAME}; print(${module_NAME}.__path__[0])"
          RESULT_VARIABLE result
          OUTPUT_VARIABLE output
          ERROR_VARIABLE error
        )
        if(result EQUAL 0)
          string(STRIP "${output}" output)
          set(PythonModule_${module_NAME}_PATH "${output}"
           CACHE PATH "Python module ${module_NAME}" FORCE)
        endif()
      endif()
    endif()
  endif()

  set(required_vars PythonModule_${module_NAME}_PATH)
  set(include_vars)
  set(library_vars)
  if(PythonModule_${module_NAME}_PATH)
    # Locate interface includes
    foreach(inc IN LISTS module_INCLUDES)
      __get_filename_label("${inc}" inc_label)
      set(inc_var PythonModule_${module_NAME}_${inc_label}_INCLUDE_DIR)
      list(APPEND include_vars ${inc_var})
      find_path(${inc_var} ${inc}
        HINTS ${PythonModule_${module_NAME}_PATH}/include
        NO_CMAKE_FIND_ROOT_PATH NO_DEFAULT_PATH
      )
    endforeach()

    # Locate interface libraries
    foreach(lib IN LISTS module_LIBRARY)
      __get_filename_label("${lib}" lib_label)
      set(lib_var PythonModule_${module_NAME}_${lib_label}_LIBRARY)
      list(APPEND library_vars ${lib_var})
      find_file(${lib_var} ${lib}
        HINTS ${PythonModule_${module_NAME}_PATH}
        NO_CMAKE_FIND_ROOT_PATH NO_DEFAULT_PATH
      )
    endforeach()
    list(APPEND required_vars ${include_vars} ${library_vars})
  endif()

  include(FindPackageHandleStandardArgs)
  find_package_handle_standard_args(PythonModule_${module_NAME} DEFAULT_MSG
    ${required_vars}
  )
endif()

if(PythonModule_${module_NAME}_FOUND AND
  NOT TARGET ${module_NAME})
  add_library(${module_NAME} INTERFACE)
  add_library(PythonModule::${module_NAME} ALIAS ${module_NAME})
  foreach(inc_var IN LISTS include_vars)
    target_include_directories(${module_NAME} INTERFACE ${${inc_var}})
  endforeach()
  foreach(lib_var IN LISTS library_vars)
    target_link_libraries(${module_NAME} INTERFACE ${${lib_var}})
  endforeach()
endif()