summaryrefslogtreecommitdiff
path: root/cmake
diff options
context:
space:
mode:
authormakaimann <makaim@stanford.edu>2020-02-19 13:54:17 -0800
committerGitHub <noreply@github.com>2020-02-19 15:54:17 -0600
commitc82720479efcf922136f0919f6fc26a502b2515a (patch)
treef9007e124cfc07490e914ae1e1e05747e1e1ee11 /cmake
parentc6a9ab9da205df7cbf192edc142ee151404dcb1b (diff)
Add Python bindings using Cython -- see below for more details (#2879)
Diffstat (limited to 'cmake')
-rw-r--r--cmake/FindCython.cmake77
-rw-r--r--cmake/FindPythonExtensions.cmake503
-rw-r--r--cmake/UseCython.cmake403
-rw-r--r--cmake/targetLinkLibrariesWithDynamicLookup.cmake478
4 files changed, 1461 insertions, 0 deletions
diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake
new file mode 100644
index 000000000..6294d2429
--- /dev/null
+++ b/cmake/FindCython.cmake
@@ -0,0 +1,77 @@
+#.rst:
+# FindCython
+# ----------
+#
+# Find ``cython`` executable.
+#
+# This module defines the following variables:
+#
+# ``CYTHON_EXECUTABLE``
+# path to the ``cython`` program
+#
+# ``CYTHON_VERSION``
+# version of ``cython``
+#
+# ``CYTHON_FOUND``
+# true if the program was found
+#
+# See also UseCython.cmake
+#
+#=============================================================================
+# 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.
+#=============================================================================
+
+# Use the Cython executable that lives next to the Python executable
+# if it is a local installation.
+find_package(PythonInterp)
+if(PYTHONINTERP_FOUND)
+ get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH)
+ find_program(CYTHON_EXECUTABLE
+ NAMES cython cython.bat cython3
+ HINTS ${_python_path}
+ DOC "path to the cython executable")
+else()
+ find_program(CYTHON_EXECUTABLE
+ NAMES cython cython.bat cython3
+ DOC "path to the cython executable")
+endif()
+
+if(CYTHON_EXECUTABLE)
+ set(CYTHON_version_command ${CYTHON_EXECUTABLE} --version)
+
+ execute_process(COMMAND ${CYTHON_version_command}
+ OUTPUT_VARIABLE CYTHON_version_output
+ ERROR_VARIABLE CYTHON_version_error
+ RESULT_VARIABLE CYTHON_version_result
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if(NOT ${CYTHON_version_result} EQUAL 0)
+ set(_error_msg "Command \"${CYTHON_version_command}\" failed with")
+ set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}")
+ message(SEND_ERROR "${_error_msg}")
+ else()
+ if("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)")
+ set(CYTHON_VERSION "${CMAKE_MATCH_1}")
+ endif()
+ endif()
+endif()
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cython REQUIRED_VARS CYTHON_EXECUTABLE)
+
+mark_as_advanced(CYTHON_EXECUTABLE)
+
+include(UseCython)
+
diff --git a/cmake/FindPythonExtensions.cmake b/cmake/FindPythonExtensions.cmake
new file mode 100644
index 000000000..892e17c08
--- /dev/null
+++ b/cmake/FindPythonExtensions.cmake
@@ -0,0 +1,503 @@
+#.rst
+# Define functions to create Python modules and executables.
+#
+# This file defines CMake functions to build Python extension modules and
+# stand-alone executables. To use it, first include this file.
+#
+# find_package(PythonExtensions)
+#
+# 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.
+#
+# The following functions are defined:
+#
+# python_extension_module(<Target>
+# [LINKED_MODULES_VAR <LinkedModVar>]
+# [FORWARD_DECL_MODULES_VAR <ForwardDeclModVar>])
+#
+# 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.
+#
+# 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 ``<Target>`` 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.
+#
+# Options:
+#
+# ``LINKED_MODULES_VAR <LinkedModVar>``
+# 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 <ForwardDeclModVar>``
+# 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.
+#
+#
+# python_standalone_executable(<Target>)
+#
+# 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.
+#
+# python_modules_header(<Name> [HeaderFilename]
+# [FORWARD_DECL_MODULES_LIST <ForwardDeclModList>]
+# [HEADER_OUTPUT_VAR <HeaderOutputVar>]
+# [INCLUDE_DIR_OUTPUT_VAR <IncludeDirOutputVar>])
+#
+# Generate a header file that contains the forward declarations and
+# initialization routines for the given list of Python extension modules.
+# ``<Name>`` is the logical name for the header file (no file extensions).
+# ``<HeaderFilename>`` is the actual destination filename for the header file
+# (e.g.: decl_modules.h).
+#
+# If only ``<Name>`` is provided, and it ends in the ".h" extension, then it
+# is assumed to be the ``<HeaderFilename>``. The filename of the header file
+# without the extension is used as the logical name. If only ``<Name>`` is
+# provided, and it does not end in the ".h" extension, then the
+# ``<HeaderFilename>`` is assumed to ``<Name>.h``.
+#
+# The exact contents of the generated header file depend on the logical
+# ``<Name>``. 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 <Name>_<Module>(void)``
+# Initializes the python extension module, ``<Module>``. Returns an integer
+# handle to the module.
+#
+# ``void <Name>_LoadAllPythonModules(void)``
+# Initializes all listed python extension modules.
+#
+# ``void CMakeLoadAllPythonModules(void);``
+# Alias for ``<Name>_LoadAllPythonModules`` whose name does not depend on
+# ``<Name>``. 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 <ForwardDeclModList>``
+# 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 <HeaderOutputVar>``
+# Name of the variable to set to the path to the generated header file. By
+# default, ``<Name>`` is used.
+#
+# ``INCLUDE_DIR_OUTPUT_VAR <IncludeDirOutputVar>``
+# Name of the variable to set to the path to the directory containing the
+# generated header file. By default, ``<Name>_INCLUDE_DIRS`` is used.
+#
+# Defined variables:
+#
+# ``<HeaderOutputVar>``
+# The path to the generated header file
+#
+# ``<IncludeDirOutputVar>``
+# Directory containing the generated header file
+#
+# Example usage:
+#
+# .. code-block:: cmake
+#
+# find_package(PythonInterp)
+# find_package(PythonLibs)
+# 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(module1 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)
+find_package(PythonLibs)
+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,
+)))
+")
+
+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)
+
+function(python_extension_module _target)
+ set(one_ops LINKED_MODULES_VAR FORWARD_DECL_MODULES_VAR)
+ 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 AND WIN32 AND NOT CYGWIN)
+ set_target_properties(${_target} PROPERTIES SUFFIX ".pyd")
+ endif()
+
+ target_link_libraries_with_dynamic_lookup(${_target} ${PYTHON_LIBRARIES})
+ endif()
+endfunction()
+
+function(python_standalone_executable _target)
+ include_directories(${PYTHON_INCLUDE_DIRS})
+ target_link_libraries(${_target} ${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 <Python.h>\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()
+
diff --git a/cmake/UseCython.cmake b/cmake/UseCython.cmake
new file mode 100644
index 000000000..7ff4a276b
--- /dev/null
+++ b/cmake/UseCython.cmake
@@ -0,0 +1,403 @@
+#.rst
+# Define a function to create Cython modules.
+#
+# For more information on the Cython project, see http://cython.org/.
+# "Cython is a language that makes writing C extensions for the Python language
+# as easy as Python itself."
+#
+# This file defines a CMake function to build a Cython Python module.
+# To use it, first include this file.
+#
+# include(UseCython)
+#
+# The following functions are defined:
+#
+# add_cython_target(<Name> [<CythonInput>]
+# [EMBED_MAIN]
+# [C | CXX]
+# [PY2 | PY3]
+# [OUTPUT_VAR <OutputVar>])
+#
+# Create a custom rule to generate the source code for a Python extension module
+# using cython. ``<Name>`` is the name of the new target, and ``<CythonInput>``
+# is the path to a cython source file. Note that, despite the name, no new
+# targets are created by this function. Instead, see ``OUTPUT_VAR`` for
+# retrieving the path to the generated source for subsequent targets.
+#
+# If only ``<Name>`` is provided, and it ends in the ".pyx" extension, then it
+# is assumed to be the ``<CythonInput>``. The name of the input without the
+# extension is used as the target name. If only ``<Name>`` is provided, and it
+# does not end in the ".pyx" extension, then the ``<CythonInput>`` is assumed to
+# be ``<Name>.pyx``.
+#
+# The Cython include search path is amended with any entries found in the
+# ``INCLUDE_DIRECTORIES`` property of the directory containing the
+# ``<CythonInput>`` file. Use ``iunclude_directories`` to add to the Cython
+# include search path.
+#
+# Options:
+#
+# ``EMBED_MAIN``
+# Embed a main() function in the generated output (for stand-alone
+# applications that initialize their own Python runtime).
+#
+# ``C | CXX``
+# Force the generation of either a C or C++ file. By default, a C file is
+# generated, unless the C language is not enabled for the project; in this
+# case, a C++ file is generated by default.
+#
+# ``PY2 | PY3``
+# Force compilation using either Python-2 or Python-3 syntax and code
+# semantics. By default, Python-2 syntax and semantics are used if the major
+# version of Python found is 2. Otherwise, Python-3 syntax and sematics are
+# used.
+#
+# ``OUTPUT_VAR <OutputVar>``
+# Set the variable ``<OutputVar>`` in the parent scope to the path to the
+# generated source file. By default, ``<Name>`` is used as the output
+# variable name.
+#
+# Defined variables:
+#
+# ``<OutputVar>``
+# The path of the generated source file.
+#
+#
+# Example usage:
+#
+# .. code-block:: cmake
+#
+# find_package(Cython)
+#
+# # Note: In this case, either one of these arguments may be omitted; their
+# # value would have been inferred from that of the other.
+# add_cython_target(cy_code cy_code.pyx)
+#
+# add_library(cy_code MODULE ${cy_code})
+# target_link_libraries(cy_code ...)
+#
+# Cache variables that effect the behavior include:
+#
+# ``CYTHON_ANNOTATE``
+# whether to create an annotated .html file when compiling
+#
+# ``CYTHON_FLAGS``
+# additional flags to pass to the Cython compiler
+#
+# See also FindCython.cmake
+#
+#=============================================================================
+# 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.
+#=============================================================================
+
+# Configuration options.
+set(CYTHON_ANNOTATE OFF
+ CACHE BOOL "Create an annotated .html file when compiling *.pyx.")
+
+set(CYTHON_FLAGS "" CACHE STRING
+ "Extra flags to the cython compiler.")
+mark_as_advanced(CYTHON_ANNOTATE CYTHON_FLAGS)
+
+find_package(PythonLibs REQUIRED)
+
+set(CYTHON_CXX_EXTENSION "cxx")
+set(CYTHON_C_EXTENSION "c")
+
+get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+
+function(add_cython_target _name)
+ set(options EMBED_MAIN C CXX PY2 PY3)
+ set(options1 OUTPUT_VAR)
+ cmake_parse_arguments(_args "${options}" "${options1}" "" ${ARGN})
+
+ list(GET _args_UNPARSED_ARGUMENTS 0 _arg0)
+
+ # if provided, 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}.pyx
+ else()
+ set(_source_file ${_name}.pyx)
+ endif()
+ endif()
+
+ set(_embed_main FALSE)
+
+ if("C" IN_LIST languages)
+ set(_output_syntax "C")
+ elseif("CXX" IN_LIST languages)
+ set(_output_syntax "CXX")
+ else()
+ message(FATAL_ERROR "Either C or CXX must be enabled to use Cython")
+ endif()
+
+ if("${PYTHONLIBS_VERSION_STRING}" MATCHES "^2.")
+ set(_input_syntax "PY2")
+ else()
+ set(_input_syntax "PY3")
+ endif()
+
+ if(_args_EMBED_MAIN)
+ set(_embed_main TRUE)
+ endif()
+
+ if(_args_C)
+ set(_output_syntax "C")
+ endif()
+
+ if(_args_CXX)
+ set(_output_syntax "CXX")
+ endif()
+
+ if(_args_PY2)
+ set(_input_syntax "PY2")
+ endif()
+
+ if(_args_PY3)
+ set(_input_syntax "PY3")
+ endif()
+
+ set(embed_arg "")
+ if(_embed_main)
+ set(embed_arg "--embed")
+ endif()
+
+ set(cxx_arg "")
+ set(extension "c")
+ if(_output_syntax STREQUAL "CXX")
+ set(cxx_arg "--cplus")
+ set(extension "cxx")
+ endif()
+
+ set(py_version_arg "")
+ if(_input_syntax STREQUAL "PY2")
+ set(py_version_arg "-2")
+ elseif(_input_syntax STREQUAL "PY3")
+ set(py_version_arg "-3")
+ endif()
+
+ set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}.${extension}")
+ set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE)
+
+ set(_output_var ${_name})
+ if(_args_OUTPUT_VAR)
+ set(_output_var ${_args_OUTPUT_VAR})
+ endif()
+ set(${_output_var} ${generated_file} PARENT_SCOPE)
+
+ file(RELATIVE_PATH generated_file_relative
+ ${CMAKE_BINARY_DIR} ${generated_file})
+
+ set(comment "Generating ${_output_syntax} source ${generated_file_relative}")
+ set(cython_include_directories "")
+ set(pxd_dependencies "")
+ set(c_header_dependencies "")
+
+ # Get the include directories.
+ get_source_file_property(pyx_location ${_source_file} LOCATION)
+ get_filename_component(pyx_path ${pyx_location} PATH)
+ get_directory_property(cmake_include_directories
+ DIRECTORY ${pyx_path}
+ INCLUDE_DIRECTORIES)
+ list(APPEND cython_include_directories ${cmake_include_directories})
+
+ # Determine dependencies.
+ # Add the pxd file with the same basename as the given pyx file.
+ get_filename_component(pyx_file_basename ${_source_file} NAME_WE)
+ unset(corresponding_pxd_file CACHE)
+ find_file(corresponding_pxd_file ${pyx_file_basename}.pxd
+ PATHS "${pyx_path}" ${cmake_include_directories}
+ NO_DEFAULT_PATH)
+ if(corresponding_pxd_file)
+ list(APPEND pxd_dependencies "${corresponding_pxd_file}")
+ endif()
+
+ # pxd files to check for additional dependencies
+ set(pxds_to_check "${_source_file}" "${pxd_dependencies}")
+ set(pxds_checked "")
+ set(number_pxds_to_check 1)
+ while(number_pxds_to_check GREATER 0)
+ foreach(pxd ${pxds_to_check})
+ list(APPEND pxds_checked "${pxd}")
+ list(REMOVE_ITEM pxds_to_check "${pxd}")
+
+ # look for C headers
+ file(STRINGS "${pxd}" extern_from_statements
+ REGEX "cdef[ ]+extern[ ]+from.*$")
+ foreach(statement ${extern_from_statements})
+ # Had trouble getting the quote in the regex
+ string(REGEX REPLACE
+ "cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1"
+ header "${statement}")
+ unset(header_location CACHE)
+ find_file(header_location ${header} PATHS ${cmake_include_directories})
+ if(header_location)
+ list(FIND c_header_dependencies "${header_location}" header_idx)
+ if(${header_idx} LESS 0)
+ list(APPEND c_header_dependencies "${header_location}")
+ endif()
+ endif()
+ endforeach()
+
+ # check for pxd dependencies
+ # Look for cimport statements.
+ set(module_dependencies "")
+ file(STRINGS "${pxd}" cimport_statements REGEX cimport)
+ foreach(statement ${cimport_statements})
+ if(${statement} MATCHES from)
+ string(REGEX REPLACE
+ "from[ ]+([^ ]+).*" "\\1"
+ module "${statement}")
+ else()
+ string(REGEX REPLACE
+ "cimport[ ]+([^ ]+).*" "\\1"
+ module "${statement}")
+ endif()
+ list(APPEND module_dependencies ${module})
+ endforeach()
+
+ # check for pxi dependencies
+ # Look for include statements.
+ set(include_dependencies "")
+ file(STRINGS "${pxd}" include_statements REGEX include)
+ foreach(statement ${include_statements})
+ string(REGEX REPLACE
+ "include[ ]+[\"]([^\"]+)[\"].*" "\\1"
+ module "${statement}")
+ list(APPEND include_dependencies ${module})
+ endforeach()
+
+ list(REMOVE_DUPLICATES module_dependencies)
+ list(REMOVE_DUPLICATES include_dependencies)
+
+ # Add modules to the files to check, if appropriate.
+ foreach(module ${module_dependencies})
+ unset(pxd_location CACHE)
+ find_file(pxd_location ${module}.pxd
+ PATHS "${pyx_path}" ${cmake_include_directories}
+ NO_DEFAULT_PATH)
+ if(pxd_location)
+ list(FIND pxds_checked ${pxd_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(FIND pxds_to_check ${pxd_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(APPEND pxds_to_check ${pxd_location})
+ list(APPEND pxd_dependencies ${pxd_location})
+ endif() # if it is not already going to be checked
+ endif() # if it has not already been checked
+ endif() # if pxd file can be found
+ endforeach() # for each module dependency discovered
+
+ # Add includes to the files to check, if appropriate.
+ foreach(_include ${include_dependencies})
+ unset(pxi_location CACHE)
+ find_file(pxi_location ${_include}
+ PATHS "${pyx_path}" ${cmake_include_directories}
+ NO_DEFAULT_PATH)
+ if(pxi_location)
+ list(FIND pxds_checked ${pxi_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(FIND pxds_to_check ${pxi_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(APPEND pxds_to_check ${pxi_location})
+ list(APPEND pxd_dependencies ${pxi_location})
+ endif() # if it is not already going to be checked
+ endif() # if it has not already been checked
+ endif() # if include file can be found
+ endforeach() # for each include dependency discovered
+ endforeach() # for each include file to check
+
+ list(LENGTH pxds_to_check number_pxds_to_check)
+ endwhile()
+
+ # Set additional flags.
+ set(annotate_arg "")
+ if(CYTHON_ANNOTATE)
+ set(annotate_arg "--annotate")
+ endif()
+
+ set(no_docstrings_arg "")
+ if(CMAKE_BUILD_TYPE STREQUAL "Release" OR
+ CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
+ set(no_docstrings_arg "--no-docstrings")
+ endif()
+
+ set(cython_debug_arg "")
+ set(embed_pos_arg "")
+ set(line_directives_arg "")
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR
+ CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
+ set(cython_debug_arg "--gdb")
+ set(embed_pos_arg "--embed-positions")
+ set(line_directives_arg "--line-directives")
+ endif()
+
+ # Include directory arguments.
+ list(REMOVE_DUPLICATES cython_include_directories)
+ set(include_directory_arg "")
+ foreach(_include_dir ${cython_include_directories})
+ set(include_directory_arg
+ ${include_directory_arg} "--include-dir" "${_include_dir}")
+ endforeach()
+
+ list(REMOVE_DUPLICATES pxd_dependencies)
+ list(REMOVE_DUPLICATES c_header_dependencies)
+
+ # Add the command to run the compiler.
+ add_custom_command(OUTPUT ${generated_file}
+ COMMAND ${CYTHON_EXECUTABLE}
+ ARGS ${cxx_arg} ${include_directory_arg} ${py_version_arg}
+ ${embed_arg} ${annotate_arg} ${no_docstrings_arg}
+ ${cython_debug_arg} ${embed_pos_arg}
+ ${line_directives_arg} ${CYTHON_FLAGS} ${pyx_location}
+ --output-file ${generated_file}
+ DEPENDS ${_source_file}
+ ${pxd_dependencies}
+ IMPLICIT_DEPENDS ${_output_syntax}
+ ${c_header_dependencies}
+ COMMENT ${comment})
+
+ # NOTE(opadron): I thought about making a proper target, but after trying it
+ # out, I decided that it would be far too convenient to use the same name as
+ # the target for the extension module (e.g.: for single-file modules):
+ #
+ # ...
+ # add_cython_target(_module.pyx)
+ # add_library(_module ${_module})
+ # ...
+ #
+ # The above example would not be possible since the "_module" target name
+ # would already be taken by the cython target. Since I can't think of a
+ # reason why someone would need the custom target instead of just using the
+ # generated file directly, I decided to leave this commented out.
+ #
+ # add_custom_target(${_name} DEPENDS ${generated_file})
+
+ # Remove their visibility to the user.
+ set(corresponding_pxd_file "" CACHE INTERNAL "")
+ set(header_location "" CACHE INTERNAL "")
+ set(pxd_location "" CACHE INTERNAL "")
+endfunction()
+
diff --git a/cmake/targetLinkLibrariesWithDynamicLookup.cmake b/cmake/targetLinkLibrariesWithDynamicLookup.cmake
new file mode 100644
index 000000000..60a0a1956
--- /dev/null
+++ b/cmake/targetLinkLibrariesWithDynamicLookup.cmake
@@ -0,0 +1,478 @@
+#
+# - This module provides the function
+# target_link_libraries_with_dynamic_lookup which can be used to
+# "weakly" link a loadable module.
+#
+# Link a library to a target such that the symbols are resolved at
+# run-time not link-time. This should be used when compiling a
+# loadable module when the symbols should be resolve from the run-time
+# environment where the module is loaded, and not a specific system
+# library.
+#
+# Specifically, for OSX it uses undefined dynamic_lookup. This is
+# similar to using "-shared" on Linux where undefined symbols are
+# ignored.
+#
+# Additionally, the linker is checked to see if it supports undefined
+# symbols when linking a shared library. If it does then the library
+# is not linked when specified with this function.
+#
+# http://blog.tim-smith.us/2015/09/python-extension-modules-os-x/
+#
+#
+# The following functions are defined:
+#
+# _get_target_type(<ResultVar> <Target>)
+#
+# **INTERNAL** Shorthand for querying an abbreviated version of the target type
+# of the given ``<Target>``. ``<ResultVar>`` is set to "STATIC" for a
+# STATIC_LIBRARY, "SHARED" for a SHARED_LIBRARY, "MODULE" for a MODULE_LIBRARY,
+# and "EXE" for an EXECUTABLE.
+#
+# Defined variables:
+#
+# ``<ResultVar>``
+# The abbreviated version of the ``<Target>``'s type.
+#
+#
+# _test_weak_link_project(<TargetType>
+# <LibType>
+# <ResultVar>
+# <LinkFlagsVar>)
+#
+# **INTERNAL** Attempt to compile and run a test project where a target of type
+# ``<TargetType>`` is weakly-linked against a dependency of type ``<LibType>``.
+# ``<TargetType>`` can be one of "STATIC", "SHARED", "MODULE", or "EXE".
+# ``<LibType>`` can be one of "STATIC", "SHARED", or "MODULE".
+#
+# Defined variables:
+#
+# ``<ResultVar>``
+# Whether the current C toolchain can produce a working target binary of type
+# ``<TargetType>`` that is weakly-linked against a dependency target of type
+# ``<LibType>``.
+#
+# ``<LinkFlagsVar>``
+# List of flags to add to the linker command to produce a working target
+# binary of type ``<TargetType>`` that is weakly-linked against a dependency
+# target of type ``<LibType>``.
+#
+#
+# check_dynamic_lookup(<TargetType>
+# <LibType>
+# <ResultVar>
+# <LinkFlagsVar>)
+#
+# Check if the linker requires a command line flag to allow leaving symbols
+# unresolved when producing a target of type ``<TargetType>`` that is
+# weakly-linked against a dependency of type ``<LibType>``. ``<TargetType>``
+# can be one of "STATIC", "SHARED", "MODULE", or "EXE". ``<LibType>`` can be
+# one of "STATIC", "SHARED", or "MODULE". The result is cached between
+# invocations and recomputed only when the value of CMake's linker flag list
+# changes; ``CMAKE_STATIC_LINKER_FLAGS`` if ``<TargetType>`` is "STATIC", and
+# ``CMAKE_SHARED_LINKER_FLAGS`` otherwise.
+#
+#
+# Defined variables:
+#
+# ``<ResultVar>``
+# Whether the current C toolchain supports weak-linking for target binaries of
+# type ``<TargetType>`` that are weakly-linked against a dependency target of
+# type ``<LibType>``.
+#
+# ``<LinkFlagsVar>``
+# List of flags to add to the linker command to produce a working target
+# binary of type ``<TargetType>`` that is weakly-linked against a dependency
+# target of type ``<LibType>``.
+#
+# ``HAS_DYNAMIC_LOOKUP_<TargetType>_<LibType>``
+# Cached, global alias for ``<ResultVar>``
+#
+# ``DYNAMIC_LOOKUP_FLAGS_<TargetType>_<LibType>``
+# Cached, global alias for ``<LinkFlagsVar>``
+#
+#
+# target_link_libraries_with_dynamic_lookup(<Target> [<Libraries>])
+#
+# Like proper linking, except that the given ``<Libraries>`` are not necessarily
+# linked. Instead, the ``<Target>`` is produced in a manner that allows for
+# symbols unresolved within it to be resolved at runtime, presumably by the
+# given ``<Libraries>``. If such a target can be produced, the provided
+# ``<Libraries>`` are not actually linked. On platforms that do not support
+# weak-linking, this function works just like ``target_link_libraries``.
+
+function(_get_target_type result_var target)
+ set(target_type "SHARED_LIBRARY")
+ if(TARGET ${target})
+ get_property(target_type TARGET ${target} PROPERTY TYPE)
+ endif()
+
+ set(result "STATIC")
+
+ if(target_type STREQUAL "STATIC_LIBRARY")
+ set(result "STATIC")
+ endif()
+
+ if(target_type STREQUAL "SHARED_LIBRARY")
+ set(result "SHARED")
+ endif()
+
+ if(target_type STREQUAL "MODULE_LIBRARY")
+ set(result "MODULE")
+ endif()
+
+ if(target_type STREQUAL "EXECUTABLE")
+ set(result "EXE")
+ endif()
+
+ set(${result_var} ${result} PARENT_SCOPE)
+endfunction()
+
+
+function(_test_weak_link_project
+ target_type
+ lib_type
+ can_weak_link_var
+ project_name)
+
+ set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all")
+ set(osx_dynamic_lookup "-undefined dynamic_lookup")
+ set(no_flag "")
+
+ foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag)
+ set(link_flag "${${link_flag_spec}}")
+
+ set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp")
+ set(test_project_dir "${test_project_dir}/${project_name}")
+ set(test_project_dir "${test_project_dir}/${link_flag_spec}")
+ set(test_project_dir "${test_project_dir}/${target_type}")
+ set(test_project_dir "${test_project_dir}/${lib_type}")
+
+ set(test_project_src_dir "${test_project_dir}/src")
+ set(test_project_bin_dir "${test_project_dir}/build")
+
+ file(MAKE_DIRECTORY ${test_project_src_dir})
+ file(MAKE_DIRECTORY ${test_project_bin_dir})
+
+ set(mod_type "STATIC")
+ set(link_mod_lib TRUE)
+ set(link_exe_lib TRUE)
+ set(link_exe_mod FALSE)
+
+ if("${target_type}" STREQUAL "EXE")
+ set(link_exe_lib FALSE)
+ set(link_exe_mod TRUE)
+ else()
+ set(mod_type "${target_type}")
+ endif()
+
+ if("${mod_type}" STREQUAL "MODULE")
+ set(link_mod_lib FALSE)
+ endif()
+
+
+ file(WRITE "${test_project_src_dir}/CMakeLists.txt" "
+ cmake_minimum_required(VERSION ${CMAKE_VERSION})
+ project(${project_name} C)
+
+ include_directories(${test_project_src_dir})
+
+ add_library(number ${lib_type} number.c)
+ add_library(counter ${mod_type} counter.c)
+ ")
+
+ if("${mod_type}" STREQUAL "MODULE")
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ set_target_properties(counter PROPERTIES PREFIX \"\")
+ ")
+ endif()
+
+ if(link_mod_lib)
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(counter number)
+ ")
+ elseif(NOT link_flag STREQUAL "")
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\")
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ add_executable(main main.c)
+ ")
+
+ if(link_exe_lib)
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main number)
+ ")
+ elseif(NOT link_flag STREQUAL "")
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main \"${link_flag}\")
+ ")
+ endif()
+
+ if(link_exe_mod)
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main counter)
+ ")
+ else()
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main \"${CMAKE_DL_LIBS}\")
+ ")
+ endif()
+
+ file(WRITE "${test_project_src_dir}/number.c" "
+ #include <number.h>
+
+ static int _number;
+ void set_number(int number) { _number = number; }
+ int get_number() { return _number; }
+ ")
+
+ file(WRITE "${test_project_src_dir}/number.h" "
+ #ifndef _NUMBER_H
+ #define _NUMBER_H
+ extern void set_number(int);
+ extern int get_number(void);
+ #endif
+ ")
+
+ file(WRITE "${test_project_src_dir}/counter.c" "
+ #include <number.h>
+ int count() {
+ int result = get_number();
+ set_number(result + 1);
+ return result;
+ }
+ ")
+
+ file(WRITE "${test_project_src_dir}/counter.h" "
+ #ifndef _COUNTER_H
+ #define _COUNTER_H
+ extern int count(void);
+ #endif
+ ")
+
+ file(WRITE "${test_project_src_dir}/main.c" "
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <number.h>
+ ")
+
+ if(NOT link_exe_mod)
+ file(APPEND "${test_project_src_dir}/main.c" "
+ #include <dlfcn.h>
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/main.c" "
+ int my_count() {
+ int result = get_number();
+ set_number(result + 1);
+ return result;
+ }
+
+ int main(int argc, char **argv) {
+ int result;
+ ")
+
+ if(NOT link_exe_mod)
+ file(APPEND "${test_project_src_dir}/main.c" "
+ void *counter_module;
+ int (*count)(void);
+
+ counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL);
+ if(!counter_module) goto error;
+
+ count = dlsym(counter_module, \"count\");
+ if(!count) goto error;
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/main.c" "
+ result = count() != 0 ? EXIT_FAILURE :
+ my_count() != 1 ? EXIT_FAILURE :
+ my_count() != 2 ? EXIT_FAILURE :
+ count() != 3 ? EXIT_FAILURE :
+ count() != 4 ? EXIT_FAILURE :
+ count() != 5 ? EXIT_FAILURE :
+ my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS;
+ ")
+
+ if(NOT link_exe_mod)
+ file(APPEND "${test_project_src_dir}/main.c" "
+ goto done;
+ error:
+ fprintf(stderr, \"Error occured:\\n %s\\n\", dlerror());
+ result = 1;
+
+ done:
+ if(counter_module) dlclose(counter_module);
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/main.c" "
+ return result;
+ }
+ ")
+
+ set(_rpath_arg)
+ if(APPLE AND ${CMAKE_VERSION} VERSION_GREATER 2.8.11)
+ set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'")
+ endif()
+
+ try_compile(project_compiles
+ "${test_project_bin_dir}"
+ "${test_project_src_dir}"
+ "${project_name}"
+ CMAKE_FLAGS
+ "-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'"
+ ${_rpath_arg}
+ OUTPUT_VARIABLE compile_output)
+
+ set(project_works 1)
+ set(run_output)
+
+ if(project_compiles)
+ execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR}
+ "${test_project_bin_dir}/main"
+ WORKING_DIRECTORY "${test_project_bin_dir}"
+ RESULT_VARIABLE project_works
+ OUTPUT_VARIABLE run_output
+ ERROR_VARIABLE run_output)
+ endif()
+
+ set(test_description
+ "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})")
+
+ if(project_works EQUAL 0)
+ set(project_works TRUE)
+ message(STATUS "Performing Test ${test_description} - Success")
+ else()
+ set(project_works FALSE)
+ message(STATUS "Performing Test ${test_description} - Failed")
+ file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "Performing Test ${test_description} failed with the "
+ "following output:\n"
+ "BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n")
+ endif()
+
+ set(${can_weak_link_var} ${project_works} PARENT_SCOPE)
+ if(project_works)
+ set(${project_name} ${link_flag} PARENT_SCOPE)
+ break()
+ endif()
+ endforeach()
+endfunction()
+
+
+function(check_dynamic_lookup
+ target_type
+ lib_type
+ has_dynamic_lookup_var
+ link_flags_var)
+
+ # hash the CMAKE_FLAGS passed and check cache to know if we need to rerun
+ if("${target_type}" STREQUAL "STATIC")
+ string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}")
+ else()
+ string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}")
+ endif()
+
+ set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}")
+ set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash")
+ set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}")
+
+ if( NOT DEFINED ${cache_hash_var}
+ OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}")
+ unset(${cache_var} CACHE)
+ endif()
+
+ if(NOT DEFINED ${cache_var})
+ set(skip_test FALSE)
+
+ if(NOT CMAKE_CROSSCOMPILING)
+ set(skip_test TRUE)
+ elseif(CMAKE_CROSSCOMPILING AND CMAKE_CROSSCOMPILING_EMULATOR)
+ set(skip_test TRUE)
+ endif()
+
+ if(skip_test)
+ set(has_dynamic_lookup FALSE)
+ set(link_flags)
+ else()
+ _test_weak_link_project(${target_type}
+ ${lib_type}
+ has_dynamic_lookup
+ link_flags)
+ endif()
+
+ set(caveat " (when linking ${target_type} against ${lib_type})")
+
+ set(${cache_var} "${has_dynamic_lookup}"
+ CACHE BOOL
+ "linker supports dynamic lookup for undefined symbols${caveat}")
+
+ set(${result_var} "${link_flags}"
+ CACHE BOOL
+ "linker flags for dynamic lookup${caveat}")
+
+ set(${cache_hash_var} "${cmake_flags_hash}"
+ CACHE INTERNAL "hashed flags for ${cache_var} check")
+ endif()
+
+ set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE)
+ set(${link_flags_var} "${${result_var}}" PARENT_SCOPE)
+endfunction()
+
+
+function(target_link_libraries_with_dynamic_lookup target)
+ _get_target_type(target_type ${target})
+
+ set(link_props)
+ set(link_items)
+ set(link_libs)
+
+ foreach(lib ${ARGN})
+ _get_target_type(lib_type ${lib})
+ check_dynamic_lookup(${target_type}
+ ${lib_type}
+ has_dynamic_lookup
+ dynamic_lookup_flags)
+
+ if(has_dynamic_lookup)
+ if(dynamic_lookup_flags)
+ if("${target_type}" STREQUAL "EXE")
+ list(APPEND link_items "${dynamic_lookup_flags}")
+ else()
+ list(APPEND link_props "${dynamic_lookup_flags}")
+ endif()
+ endif()
+ else()
+ list(APPEND link_libs "${lib}")
+ endif()
+ endforeach()
+
+ if(link_props)
+ list(REMOVE_DUPLICATES link_props)
+ endif()
+
+ if(link_items)
+ list(REMOVE_DUPLICATES link_items)
+ endif()
+
+ if(link_libs)
+ list(REMOVE_DUPLICATES link_libs)
+ endif()
+
+ if(link_props)
+ set_target_properties(${target}
+ PROPERTIES LINK_FLAGS "${link_props}")
+ endif()
+
+ set(links "${link_items}" "${link_libs}")
+ if(links)
+ target_link_libraries(${target} "${links}")
+ endif()
+endfunction()
+
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback