summaryrefslogtreecommitdiff
path: root/cmake/targetLinkLibrariesWithDynamicLookup.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/targetLinkLibrariesWithDynamicLookup.cmake
parentc6a9ab9da205df7cbf192edc142ee151404dcb1b (diff)
Add Python bindings using Cython -- see below for more details (#2879)
Diffstat (limited to 'cmake/targetLinkLibrariesWithDynamicLookup.cmake')
-rw-r--r--cmake/targetLinkLibrariesWithDynamicLookup.cmake478
1 files changed, 478 insertions, 0 deletions
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