#.rst: # # This module defines CMake functions to build Python extension modules and # stand-alone executables. # # The following variables are defined: # :: # # PYTHON_PREFIX - absolute path to the current Python # distribution's prefix # PYTHON_SITE_PACKAGES_DIR - absolute path to the current Python # distribution's site-packages directory # PYTHON_RELATIVE_SITE_PACKAGES_DIR - path to the current Python # distribution's site-packages directory # relative to its prefix # PYTHON_SEPARATOR - separator string for file path # components. Equivalent to ``os.sep`` in # Python. # PYTHON_PATH_SEPARATOR - separator string for PATH-style # environment variables. Equivalent to # ``os.pathsep`` in Python. # PYTHON_EXTENSION_MODULE_SUFFIX - suffix of the compiled module. For example, on # Linux, based on environment, it could be ``.cpython-35m-x86_64-linux-gnu.so``. # # # # The following functions are defined: # # .. cmake:command:: python_extension_module # # For libraries meant to be used as Python extension modules, either dynamically # loaded or directly linked. Amend the configuration of the library target # (created using ``add_library``) with additional options needed to build and # use the referenced library as a Python extension module. # # python_extension_module( # [LINKED_MODULES_VAR ] # [FORWARD_DECL_MODULES_VAR ] # [MODULE_SUFFIX ]) # # Only extension modules that are configured to be built as MODULE libraries can # be runtime-loaded through the standard Python import mechanism. All other # modules can only be included in standalone applications that are written to # expect their presence. In addition to being linked against the libraries for # these modules, such applications must forward declare their entry points and # initialize them prior to use. To generate these forward declarations and # initializations, see ``python_modules_header``. # # If ```` does not refer to a target, then it is assumed to refer to an # extension module that is not linked at all, but compiled along with other # source files directly into an executable. Adding these modules does not cause # any library configuration modifications, and they are not added to the list of # linked modules. They still must be forward declared and initialized, however, # and so are added to the forward declared modules list. # # If the associated target is of type ``MODULE_LIBRARY``, the LINK_FLAGS target # property is used to set symbol visibility and export only the module init function. # This applies to GNU and MSVC compilers. # # Options: # # ``LINKED_MODULES_VAR `` # Name of the variable referencing a list of extension modules whose libraries # must be linked into the executables of any stand-alone applications that use # them. By default, the global property ``PY_LINKED_MODULES_LIST`` is used. # # ``FORWARD_DECL_MODULES_VAR `` # Name of the variable referencing a list of extension modules whose entry # points must be forward declared and called by any stand-alone applications # that use them. By default, the global property # ``PY_FORWARD_DECL_MODULES_LIST`` is used. # # ``MODULE_SUFFIX `` # Suffix appended to the python extension module file. # The default suffix is retrieved using ``sysconfig.get_config_var("SO")"``, # if not available, the default is then ``.so`` on unix and ``.pyd`` on # windows. # Setting the variable ``PYTHON_EXTENSION_MODULE_SUFFIX`` in the caller # scope defines the value used for all extensions not having a suffix # explicitly specified using ``MODULE_SUFFIX`` parameter. # # # .. cmake:command:: python_standalone_executable # # python_standalone_executable() # # For standalone executables that initialize their own Python runtime # (such as when building source files that include one generated by Cython with # the --embed option). Amend the configuration of the executable target # (created using ``add_executable``) with additional options needed to properly # build the referenced executable. # # # .. cmake:command:: python_modules_header # # Generate a header file that contains the forward declarations and # initialization routines for the given list of Python extension modules. # ```` is the logical name for the header file (no file extensions). # ```` is the actual destination filename for the header file # (e.g.: decl_modules.h). # # python_modules_header( [HeaderFilename] # [FORWARD_DECL_MODULES_LIST ] # [HEADER_OUTPUT_VAR ] # [INCLUDE_DIR_OUTPUT_VAR ]) # # without the extension is used as the logical name. If only ```` is # # If only ```` is provided, and it ends in the ".h" extension, then it # is assumed to be the ````. The filename of the header file # provided, and it does not end in the ".h" extension, then the # ```` is assumed to ``.h``. # # The exact contents of the generated header file depend on the logical # ````. It should be set to a value that corresponds to the target # application, or for the case of multiple applications, some identifier that # conveyes its purpose. It is featured in the generated multiple inclusion # guard as well as the names of the generated initialization routines. # # The generated header file includes forward declarations for all listed # modules, as well as implementations for the following class of routines: # # ``int _(void)`` # Initializes the python extension module, ````. Returns an integer # handle to the module. # # ``void _LoadAllPythonModules(void)`` # Initializes all listed python extension modules. # # ``void CMakeLoadAllPythonModules(void);`` # Alias for ``_LoadAllPythonModules`` whose name does not depend on # ````. This function is excluded during preprocessing if the # preprocessing macro ``EXCLUDE_LOAD_ALL_FUNCTION`` is defined. # # ``void Py_Initialize_Wrapper();`` # Wrapper arpund ``Py_Initialize()`` that initializes all listed python # extension modules. This function is excluded during preprocessing if the # preprocessing macro ``EXCLUDE_PY_INIT_WRAPPER`` is defined. If this # function is generated, then ``Py_Initialize()`` is redefined to a macro # that calls this function. # # Options: # # ``FORWARD_DECL_MODULES_LIST `` # List of extension modules for which to generate forward declarations of # their entry points and their initializations. By default, the global # property ``PY_FORWARD_DECL_MODULES_LIST`` is used. # # ``HEADER_OUTPUT_VAR `` # Name of the variable to set to the path to the generated header file. By # default, ```` is used. # # ``INCLUDE_DIR_OUTPUT_VAR `` # Name of the variable to set to the path to the directory containing the # generated header file. By default, ``_INCLUDE_DIRS`` is used. # # Defined variables: # # ```` # The path to the generated header file # # ```` # Directory containing the generated header file # # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(PythonExtensions) # find_package(Cython) # find_package(Boost COMPONENTS python) # # # Simple Cython Module -- no executables # add_cython_target(_module.pyx) # add_library(_module MODULE ${_module}) # python_extension_module(_module) # # # Mix of Cython-generated code and C++ code using Boost Python # # Stand-alone executable -- no modules # include_directories(${Boost_INCLUDE_DIRS}) # add_cython_target(main.pyx CXX EMBED_MAIN) # add_executable(main boost_python_module.cxx ${main}) # target_link_libraries(main ${Boost_LIBRARIES}) # python_standalone_executable(main) # # # stand-alone executable with three extension modules: # # one statically linked, one dynamically linked, and one loaded at runtime # # # # Freely mixes Cython-generated code, code using Boost-Python, and # # hand-written code using the CPython API. # # # module1 -- statically linked # add_cython_target(module1.pyx) # add_library(module1 STATIC ${module1}) # python_extension_module(module1 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # module2 -- dynamically linked # include_directories(${Boost_INCLUDE_DIRS}) # add_library(module2 SHARED boost_module2.cxx) # target_link_libraries(module2 ${Boost_LIBRARIES}) # python_extension_module(module2 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # module3 -- loaded at runtime # add_cython_target(module3a.pyx) # add_library(module3 MODULE ${module3a} module3b.cxx) # target_link_libraries(module3 ${Boost_LIBRARIES}) # python_extension_module(module3 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # application executable -- generated header file + other source files # python_modules_header(modules # FORWARD_DECL_MODULES_LIST ${fdecl_module_list}) # include_directories(${modules_INCLUDE_DIRS}) # # add_cython_target(mainA) # add_cython_target(mainC) # add_executable(main ${mainA} mainB.cxx ${mainC} mainD.c) # # target_link_libraries(main ${linked_module_list} ${Boost_LIBRARIES}) # python_standalone_executable(main) # #============================================================================= # Copyright 2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= find_package(PythonInterp REQUIRED) if(SKBUILD AND NOT PYTHON_LIBRARY) set(PYTHON_LIBRARY "no-library-required") find_package(PythonLibs) unset(PYTHON_LIBRARY) unset(PYTHON_LIBRARIES) else() find_package(PythonLibs) endif() include(targetLinkLibrariesWithDynamicLookup) set(_command " import distutils.sysconfig import itertools import os import os.path import site import sys result = None rel_result = None candidate_lists = [] try: candidate_lists.append((distutils.sysconfig.get_python_lib(),)) except AttributeError: pass try: candidate_lists.append(site.getsitepackages()) except AttributeError: pass try: candidate_lists.append((site.getusersitepackages(),)) except AttributeError: pass candidates = itertools.chain.from_iterable(candidate_lists) for candidate in candidates: rel_candidate = os.path.relpath( candidate, sys.prefix) if not rel_candidate.startswith(\"..\"): result = candidate rel_result = rel_candidate break sys.stdout.write(\";\".join(( os.sep, os.pathsep, sys.prefix, result, rel_result, distutils.sysconfig.get_config_var('EXT_SUFFIX') ))) ") execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "${_command}" OUTPUT_VARIABLE _list RESULT_VARIABLE _result) list(GET _list 0 _item) set(PYTHON_SEPARATOR "${_item}") mark_as_advanced(PYTHON_SEPARATOR) list(GET _list 1 _item) set(PYTHON_PATH_SEPARATOR "${_item}") mark_as_advanced(PYTHON_PATH_SEPARATOR) list(GET _list 2 _item) set(PYTHON_PREFIX "${_item}") mark_as_advanced(PYTHON_PREFIX) list(GET _list 3 _item) set(PYTHON_SITE_PACKAGES_DIR "${_item}") mark_as_advanced(PYTHON_SITE_PACKAGES_DIR) list(GET _list 4 _item) set(PYTHON_RELATIVE_SITE_PACKAGES_DIR "${_item}") mark_as_advanced(PYTHON_RELATIVE_SITE_PACKAGES_DIR) if(NOT DEFINED PYTHON_EXTENSION_MODULE_SUFFIX) list(GET _list 5 _item) set(PYTHON_EXTENSION_MODULE_SUFFIX "${_item}") endif() function(_set_python_extension_symbol_visibility _target) if(PYTHON_VERSION_MAJOR VERSION_GREATER 2) set(_modinit_prefix "PyInit_") else() set(_modinit_prefix "init") endif() message("_modinit_prefix:${_modinit_prefix}") if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") set_target_properties(${_target} PROPERTIES LINK_FLAGS "/EXPORT:${_modinit_prefix}${_target}" ) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Option to not run version script. See https://github.com/scikit-build/scikit-build/issues/668 if(NOT DEFINED SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE) set(SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE FALSE) endif() set(_script_path ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_target}-version-script.map ) # Export all symbols. See https://github.com/scikit-build/scikit-build/issues/668 if(SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE) file(WRITE ${_script_path} "{global: ${_modinit_prefix}${_target};};" ) else() file(WRITE ${_script_path} "{global: ${_modinit_prefix}${_target}; local: *;};" ) endif() if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS") set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--version-script=\"${_script_path}\"" ) else() set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-M \"${_script_path}\"" ) endif() endif() endfunction() function(python_extension_module _target) set(one_ops LINKED_MODULES_VAR FORWARD_DECL_MODULES_VAR MODULE_SUFFIX) cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) set(_lib_type "NA") if(TARGET ${_target}) get_property(_lib_type TARGET ${_target} PROPERTY TYPE) endif() set(_is_non_lib TRUE) set(_is_static_lib FALSE) if(_lib_type STREQUAL "STATIC_LIBRARY") set(_is_static_lib TRUE) set(_is_non_lib FALSE) endif() set(_is_shared_lib FALSE) if(_lib_type STREQUAL "SHARED_LIBRARY") set(_is_shared_lib TRUE) set(_is_non_lib FALSE) endif() set(_is_module_lib FALSE) if(_lib_type STREQUAL "MODULE_LIBRARY") set(_is_module_lib TRUE) set(_is_non_lib FALSE) endif() if(_is_static_lib OR _is_shared_lib OR _is_non_lib) if(_is_static_lib OR _is_shared_lib) if(_args_LINKED_MODULES_VAR) set(${_args_LINKED_MODULES_VAR} ${${_args_LINKED_MODULES_VAR}} ${_target} PARENT_SCOPE) else() set_property(GLOBAL APPEND PROPERTY PY_LINKED_MODULES_LIST ${_target}) endif() endif() if(_args_FORWARD_DECL_MODULES_VAR) set(${_args_FORWARD_DECL_MODULES_VAR} ${${_args_FORWARD_DECL_MODULES_VAR}} ${_target} PARENT_SCOPE) else() set_property(GLOBAL APPEND PROPERTY PY_FORWARD_DECL_MODULES_LIST ${_target}) endif() endif() if(NOT _is_non_lib) include_directories("${PYTHON_INCLUDE_DIRS}") endif() if(_is_module_lib) set_target_properties(${_target} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") endif() if(_is_module_lib OR _is_shared_lib) if(_is_module_lib) if(NOT _args_MODULE_SUFFIX) set(_args_MODULE_SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}") endif() if(_args_MODULE_SUFFIX STREQUAL "" AND WIN32 AND NOT CYGWIN) set(_args_MODULE_SUFFIX ".pyd") endif() if(NOT _args_MODULE_SUFFIX STREQUAL "") set_target_properties(${_target} PROPERTIES SUFFIX ${_args_MODULE_SUFFIX}) endif() endif() target_link_libraries_with_dynamic_lookup(${_target} ${PYTHON_LIBRARIES}) if(_is_module_lib) _set_python_extension_symbol_visibility(${_target}) endif() endif() endfunction() function(python_standalone_executable _target) include_directories(${PYTHON_INCLUDE_DIRS}) target_link_libraries(${_target} ${SKBUILD_LINK_LIBRARIES_KEYWORD} ${PYTHON_LIBRARIES}) endfunction() function(python_modules_header _name) set(one_ops FORWARD_DECL_MODULES_LIST HEADER_OUTPUT_VAR INCLUDE_DIR_OUTPUT_VAR) cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) # if present, use arg0 as the input file path if(_arg0) set(_source_file ${_arg0}) # otherwise, must determine source file from name, or vice versa else() get_filename_component(_name_ext "${_name}" EXT) # if extension provided, _name is the source file if(_name_ext) set(_source_file ${_name}) get_filename_component(_name "${_source_file}" NAME_WE) # otherwise, assume the source file is ${_name}.h else() set(_source_file ${_name}.h) endif() endif() if(_args_FORWARD_DECL_MODULES_LIST) set(static_mod_list ${_args_FORWARD_DECL_MODULES_LIST}) else() get_property(static_mod_list GLOBAL PROPERTY PY_FORWARD_DECL_MODULES_LIST) endif() string(REPLACE "." "_" _header_name "${_name}") string(TOUPPER ${_header_name} _header_name_upper) set(_header_name_upper "_${_header_name_upper}_H") set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${_source_file}) set(generated_file_tmp "${generated_file}.in") file(WRITE ${generated_file_tmp} "/* Created by CMake. DO NOT EDIT; changes will be lost. */\n") set(_chunk "") set(_chunk "${_chunk}#ifndef ${_header_name_upper}\n") set(_chunk "${_chunk}#define ${_header_name_upper}\n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#include \n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#ifdef __cplusplus\n") set(_chunk "${_chunk}extern \"C\" {\n") set(_chunk "${_chunk}#endif /* __cplusplus */\n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#if PY_MAJOR_VERSION < 3\n") file(APPEND ${generated_file_tmp} "${_chunk}") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} "PyMODINIT_FUNC init${PYTHON_MODULE_PREFIX}${_module}(void);\n") endforeach() file(APPEND ${generated_file_tmp} "#else /* PY_MAJOR_VERSION >= 3*/\n") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} "PyMODINIT_FUNC PyInit_${PYTHON_MODULE_PREFIX}${_module}(void);\n") endforeach() set(_chunk "") set(_chunk "${_chunk}#endif /* PY_MAJOR_VERSION >= 3*/\n\n") set(_chunk "${_chunk}#ifdef __cplusplus\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#endif /* __cplusplus */\n") set(_chunk "${_chunk}\n") file(APPEND ${generated_file_tmp} "${_chunk}") foreach(_module ${static_mod_list}) set(_import_function "${_header_name}_${_module}") set(_prefixed_module "${PYTHON_MODULE_PREFIX}${_module}") set(_chunk "") set(_chunk "${_chunk}int ${_import_function}(void)\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} static char name[] = \"${_prefixed_module}\";\n") set(_chunk "${_chunk} #if PY_MAJOR_VERSION < 3\n") set(_chunk "${_chunk} return PyImport_AppendInittab(") set(_chunk "${_chunk}name, init${_prefixed_module});\n") set(_chunk "${_chunk} #else /* PY_MAJOR_VERSION >= 3 */\n") set(_chunk "${_chunk} return PyImport_AppendInittab(") set(_chunk "${_chunk}name, PyInit_${_prefixed_module});\n") set(_chunk "${_chunk} #endif /* PY_MAJOR_VERSION >= 3 */\n") set(_chunk "${_chunk}}\n\n") file(APPEND ${generated_file_tmp} "${_chunk}") endforeach() file(APPEND ${generated_file_tmp} "void ${_header_name}_LoadAllPythonModules(void)\n{\n") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} " ${_header_name}_${_module}();\n") endforeach() file(APPEND ${generated_file_tmp} "}\n\n") set(_chunk "") set(_chunk "${_chunk}#ifndef EXCLUDE_LOAD_ALL_FUNCTION\n") set(_chunk "${_chunk}void CMakeLoadAllPythonModules(void)\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#endif /* !EXCLUDE_LOAD_ALL_FUNCTION */\n\n") set(_chunk "${_chunk}#ifndef EXCLUDE_PY_INIT_WRAPPER\n") set(_chunk "${_chunk}static void Py_Initialize_Wrapper()\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") set(_chunk "${_chunk} Py_Initialize();\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#define Py_Initialize Py_Initialize_Wrapper\n") set(_chunk "${_chunk}#endif /* !EXCLUDE_PY_INIT_WRAPPER */\n\n") set(_chunk "${_chunk}#endif /* !${_header_name_upper} */\n") file(APPEND ${generated_file_tmp} "${_chunk}") # with configure_file() cmake complains that you may not use a file created # using file(WRITE) as input file for configure_file() execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${generated_file_tmp}" "${generated_file}" OUTPUT_QUIET ERROR_QUIET) set(_header_output_var ${_name}) if(_args_HEADER_OUTPUT_VAR) set(_header_output_var ${_args_HEADER_OUTPUT_VAR}) endif() set(${_header_output_var} ${generated_file} PARENT_SCOPE) set(_include_dir_var ${_name}_INCLUDE_DIRS) if(_args_INCLUDE_DIR_OUTPUT_VAR) set(_include_dir_var ${_args_INCLUDE_DIR_OUTPUT_VAR}) endif() set(${_include_dirs_var} ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) endfunction() include(UsePythonExtensions)