Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkTargetLinkLibrariesWithDynamicLookup.cmake
Go to the documentation of this file.
1 #.rst:
2 #
3 # Public Functions
4 # ^^^^^^^^^^^^^^^^
5 #
6 # The following functions are defined:
7 #
8 # .. cmake:command:: mitk_target_link_libraries_with_dynamic_lookup
9 #
10 # ::
11 #
12 # mitk_target_link_libraries_with_dynamic_lookup(<Target> [<Libraries>])
13 #
14 #
15 # Useful to "weakly" link a loadable module. For example, it should be used
16 # when compiling a loadable module when the symbols should be resolve from
17 # the run-time environment where the module is loaded, and not a specific
18 # system library.
19 #
20 # Like proper linking, except that the given ``<Libraries>`` are not necessarily
21 # linked. Instead, the ``<Target>`` is produced in a manner that allows for
22 # symbols unresolved within it to be resolved at runtime, presumably by the
23 # given ``<Libraries>``. If such a target can be produced, the provided
24 # ``<Libraries>`` are not actually linked.
25 #
26 # It links a library to a target such that the symbols are resolved at
27 # run-time not link-time.
28 #
29 # The linker is checked to see if it supports undefined
30 # symbols when linking a shared library. If it does then the library
31 # is not linked when specified with this function.
32 #
33 # On platforms that do not support weak-linking, this function works just
34 # like ``mitk_target_link_libraries``.
35 #
36 # .. note::
37 #
38 # For OSX it uses ``undefined dynamic_lookup``. This is similar to using
39 # ``-shared`` on Linux where undefined symbols are ignored.
40 #
41 # For more details, see `blog <http://blog.tim-smith.us/2015/09/python-extension-modules-os-x/>`_
42 # from Tim D. Smith.
43 #
44 #
45 # .. cmake:command:: mitk_check_dynamic_lookup
46 #
47 # Check if the linker requires a command line flag to allow leaving symbols
48 # unresolved when producing a target of type ``<TargetType>`` that is
49 # weakly-linked against a dependency of type ``<LibType>``.
50 #
51 # ``<TargetType>``
52 # can be one of "STATIC", "SHARED", "MODULE", or "EXE".
53 #
54 # ``<LibType>``
55 # can be one of "STATIC", "SHARED", or "MODULE".
56 #
57 # Long signature:
58 #
59 # ::
60 #
61 # mitk_check_dynamic_lookup(<TargetType>
62 # <LibType>
63 # <ResultVar>
64 # [<LinkFlagsVar>])
65 #
66 #
67 # Short signature:
68 #
69 # ::
70 #
71 # mitk_check_dynamic_lookup(<ResultVar>) # <TargetType> set to "MODULE"
72 # # <LibType> set to "SHARED"
73 #
74 #
75 # The result is cached between invocations and recomputed only when the value
76 # of CMake's linker flag list changes; ``CMAKE_STATIC_LINKER_FLAGS`` if
77 # ``<TargetType>`` is "STATIC", and ``CMAKE_SHARED_LINKER_FLAGS`` otherwise.
78 #
79 #
80 # Defined variables:
81 #
82 # ``<ResultVar>``
83 # Whether the current C toolchain supports weak-linking for target binaries of
84 # type ``<TargetType>`` that are weakly-linked against a dependency target of
85 # type ``<LibType>``.
86 #
87 # ``<LinkFlagsVar>``
88 # List of flags to add to the linker command to produce a working target
89 # binary of type ``<TargetType>`` that is weakly-linked against a dependency
90 # target of type ``<LibType>``.
91 #
92 # ``HAS_DYNAMIC_LOOKUP_<TargetType>_<LibType>``
93 # Cached, global alias for ``<ResultVar>``
94 #
95 # ``DYNAMIC_LOOKUP_FLAGS_<TargetType>_<LibType>``
96 # Cached, global alias for ``<LinkFlagsVar>``
97 #
98 #
99 # Private Functions
100 # ^^^^^^^^^^^^^^^^^
101 #
102 # The following private functions are defined:
103 #
104 # .. warning:: These functions are not part of the scikit-build API. They
105 # exist purely as an implementation detail and may change from version
106 # to version without notice, or even be removed.
107 #
108 # We mean it.
109 #
110 #
111 # .. cmake:command:: _get_target_type
112 #
113 # ::
114 #
115 # _get_target_type(<ResultVar> <Target>)
116 #
117 #
118 # Shorthand for querying an abbreviated version of the target type
119 # of the given ``<Target>``.
120 #
121 # ``<ResultVar>`` is set to:
122 #
123 # - "STATIC" for a STATIC_LIBRARY,
124 # - "SHARED" for a SHARED_LIBRARY,
125 # - "MODULE" for a MODULE_LIBRARY,
126 # - and "EXE" for an EXECUTABLE.
127 #
128 # Defined variables:
129 #
130 # ``<ResultVar>``
131 # The abbreviated version of the ``<Target>``'s type.
132 #
133 #
134 # .. cmake:command:: _test_weak_link_project
135 #
136 # ::
137 #
138 # _test_weak_link_project(<TargetType>
139 # <LibType>
140 # <ResultVar>
141 # <LinkFlagsVar>)
142 #
143 #
144 # Attempt to compile and run a test project where a target of type
145 # ``<TargetType>`` is weakly-linked against a dependency of type ``<LibType>``:
146 #
147 # - ``<TargetType>`` can be one of "STATIC", "SHARED", "MODULE", or "EXE".
148 # - ``<LibType>`` can be one of "STATIC", "SHARED", or "MODULE".
149 #
150 # Defined variables:
151 #
152 # ``<ResultVar>``
153 # Whether the current C toolchain can produce a working target binary of type
154 # ``<TargetType>`` that is weakly-linked against a dependency target of type
155 # ``<LibType>``.
156 #
157 # ``<LinkFlagsVar>``
158 # List of flags to add to the linker command to produce a working target
159 # binary of type ``<TargetType>`` that is weakly-linked against a dependency
160 # target of type ``<LibType>``.
161 #
162 
163 function(_get_target_type result_var target)
164  set(target_type "SHARED_LIBRARY")
165  if(TARGET ${target})
166  get_property(target_type TARGET ${target} PROPERTY TYPE)
167  endif()
168 
169  set(result "STATIC")
170 
171  if(target_type STREQUAL "STATIC_LIBRARY")
172  set(result "STATIC")
173  endif()
174 
175  if(target_type STREQUAL "SHARED_LIBRARY")
176  set(result "SHARED")
177  endif()
178 
179  if(target_type STREQUAL "MODULE_LIBRARY")
180  set(result "MODULE")
181  endif()
182 
183  if(target_type STREQUAL "EXECUTABLE")
184  set(result "EXE")
185  endif()
186 
187  set(${result_var} ${result} PARENT_SCOPE)
188 endfunction()
189 
190 
192  target_type
193  lib_type
194  can_weak_link_var
195  project_name)
196 
197  set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all")
198  set(osx_dynamic_lookup "-undefined dynamic_lookup")
199  set(no_flag "")
200 
201  foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag)
202  set(link_flag "${${link_flag_spec}}")
203 
204  set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp")
205  set(test_project_dir "${test_project_dir}/${project_name}")
206  set(test_project_dir "${test_project_dir}/${link_flag_spec}")
207  set(test_project_dir "${test_project_dir}/${target_type}")
208  set(test_project_dir "${test_project_dir}/${lib_type}")
209 
210  set(test_project_src_dir "${test_project_dir}/src")
211  set(test_project_bin_dir "${test_project_dir}/build")
212 
213  file(MAKE_DIRECTORY ${test_project_src_dir})
214  file(MAKE_DIRECTORY ${test_project_bin_dir})
215 
216  set(mod_type "STATIC")
217  set(link_mod_lib TRUE)
218  set(link_exe_lib TRUE)
219  set(link_exe_mod FALSE)
220 
221  if("${target_type}" STREQUAL "EXE")
222  set(link_exe_lib FALSE)
223  set(link_exe_mod TRUE)
224  else()
225  set(mod_type "${target_type}")
226  endif()
227 
228  if("${mod_type}" STREQUAL "MODULE")
229  set(link_mod_lib FALSE)
230  endif()
231 
232 
233  file(WRITE "${test_project_src_dir}/CMakeLists.txt" "
234  cmake_minimum_required(VERSION ${CMAKE_VERSION})
235  project(${project_name} C)
236 
237  include_directories(${test_project_src_dir})
238 
239  add_library(number ${lib_type} number.c)
240  add_library(counter ${mod_type} counter.c)
241  ")
242 
243  if("${mod_type}" STREQUAL "MODULE")
244  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
245  set_target_properties(counter PROPERTIES PREFIX \"\")
246  ")
247  endif()
248 
249  if(link_mod_lib)
250  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
251  target_link_libraries(counter number)
252  ")
253  elseif(NOT link_flag STREQUAL "")
254  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
255  set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\")
256  ")
257  endif()
258 
259  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
260  add_executable(main main.c)
261  ")
262 
263  if(link_exe_lib)
264  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
265  target_link_libraries(main number)
266  ")
267  elseif(NOT link_flag STREQUAL "")
268  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
269  target_link_libraries(main \"${link_flag}\")
270  ")
271  endif()
272 
273  if(link_exe_mod)
274  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
275  target_link_libraries(main counter)
276  ")
277  else()
278  file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
279  target_link_libraries(main \"${CMAKE_DL_LIBS}\")
280  ")
281  endif()
282 
283  file(WRITE "${test_project_src_dir}/number.c" "
284  #include <number.h>
285 
286  static int _number;
287  void set_number(int number) { _number = number; }
288  int get_number() { return _number; }
289  ")
290 
291  file(WRITE "${test_project_src_dir}/number.h" "
292  #ifndef _NUMBER_H
293  #define _NUMBER_H
294  extern void set_number(int);
295  extern int get_number(void);
296  #endif
297  ")
298 
299  file(WRITE "${test_project_src_dir}/counter.c" "
300  #include <number.h>
301  int count() {
302  int result = get_number();
303  set_number(result + 1);
304  return result;
305  }
306  ")
307 
308  file(WRITE "${test_project_src_dir}/counter.h" "
309  #ifndef _COUNTER_H
310  #define _COUNTER_H
311  extern int count(void);
312  #endif
313  ")
314 
315  file(WRITE "${test_project_src_dir}/main.c" "
316  #include <stdlib.h>
317  #include <stdio.h>
318  #include <number.h>
319  ")
320 
321  if(NOT link_exe_mod)
322  file(APPEND "${test_project_src_dir}/main.c" "
323  #include <dlfcn.h>
324  ")
325  endif()
326 
327  file(APPEND "${test_project_src_dir}/main.c" "
328  int my_count() {
329  int result = get_number();
330  set_number(result + 1);
331  return result;
332  }
333 
334  int main(int argc, char **argv) {
335  int result;
336  ")
337 
338  if(NOT link_exe_mod)
339  file(APPEND "${test_project_src_dir}/main.c" "
340  void *counter_module;
341  int (*count)(void);
342 
343  counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL);
344  if(!counter_module) goto error;
345 
346  count = dlsym(counter_module, \"count\");
347  if(!count) goto error;
348  ")
349  endif()
350 
351  file(APPEND "${test_project_src_dir}/main.c" "
352  result = count() != 0 ? EXIT_FAILURE :
353  my_count() != 1 ? EXIT_FAILURE :
354  my_count() != 2 ? EXIT_FAILURE :
355  count() != 3 ? EXIT_FAILURE :
356  count() != 4 ? EXIT_FAILURE :
357  count() != 5 ? EXIT_FAILURE :
358  my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS;
359  ")
360 
361  if(NOT link_exe_mod)
362  file(APPEND "${test_project_src_dir}/main.c" "
363  goto done;
364  error:
365  fprintf(stderr, \"Error occured:\\n %s\\n\", dlerror());
366  result = 1;
367 
368  done:
369  if(counter_module) dlclose(counter_module);
370  ")
371  endif()
372 
373  file(APPEND "${test_project_src_dir}/main.c" "
374  return result;
375  }
376  ")
377 
378  set(_rpath_arg)
379  if(APPLE AND ${CMAKE_VERSION} VERSION_GREATER 2.8.11)
380  set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'")
381  endif()
382 
383  try_compile(project_compiles
384  "${test_project_bin_dir}"
385  "${test_project_src_dir}"
386  "${project_name}"
387  CMAKE_FLAGS
388  "-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'"
389  "-DCMAKE_ENABLE_EXPORTS=ON"
390  ${_rpath_arg}
391  OUTPUT_VARIABLE compile_output)
392 
393  set(project_works 1)
394  set(run_output)
395 
396  if(project_compiles)
397  execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR}
398  "${test_project_bin_dir}/main"
399  WORKING_DIRECTORY "${test_project_bin_dir}"
400  RESULT_VARIABLE project_works
401  OUTPUT_VARIABLE run_output
402  ERROR_VARIABLE run_output)
403  endif()
404 
405  set(test_description
406  "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})")
407 
408  if(project_works EQUAL 0)
409  set(project_works TRUE)
410  message(STATUS "Performing Test ${test_description} - Success")
411  else()
412  set(project_works FALSE)
413  message(STATUS "Performing Test ${test_description} - Failed")
414  file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log
415  "Performing Test ${test_description} failed with the "
416  "following output:\n"
417  "BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n")
418  endif()
419 
420  set(${can_weak_link_var} ${project_works} PARENT_SCOPE)
421  if(project_works)
422  set(${project_name} ${link_flag} PARENT_SCOPE)
423  break()
424  endif()
425  endforeach()
426 endfunction()
427 
428 function(mitk_check_dynamic_lookup)
429  # Two signatures are supported:
430 
431  if(ARGC EQUAL "1")
432  #
433  # mitk_check_dynamic_lookup(<ResultVar>)
434  #
435  set(target_type "MODULE")
436  set(lib_type "SHARED")
437  set(has_dynamic_lookup_var "${ARGV0}")
438  set(link_flags_var "unused")
439 
440  elseif(ARGC GREATER "2")
441  #
442  # mitk_check_dynamic_lookup(<TargetType>
443  # <LibType>
444  # <ResultVar>
445  # [<LinkFlagsVar>])
446  #
447  set(target_type "${ARGV0}")
448  set(lib_type "${ARGV1}")
449  set(has_dynamic_lookup_var "${ARGV2}")
450  if(ARGC EQUAL "3")
451  set(link_flags_var "unused")
452  else()
453  set(link_flags_var "${ARGV3}")
454  endif()
455  else()
456  message(FATAL_ERROR "missing arguments")
457  endif()
458 
459  _check_dynamic_lookup(
460  ${target_type}
461  ${lib_type}
462  ${has_dynamic_lookup_var}
463  ${link_flags_var}
464  )
465  set(${has_dynamic_lookup_var} ${${has_dynamic_lookup_var}} PARENT_SCOPE)
466  if(NOT "x${link_flags_var}x" MATCHES "^xunusedx$")
467  set(${link_flags_var} ${${link_flags_var}} PARENT_SCOPE)
468  endif()
469 endfunction()
470 
471 function(_check_dynamic_lookup
472  target_type
473  lib_type
474  has_dynamic_lookup_var
475  link_flags_var
476  )
477 
478  # hash the CMAKE_FLAGS passed and check cache to know if we need to rerun
479  if("${target_type}" STREQUAL "STATIC")
480  string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}")
481  else()
482  string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}")
483  endif()
484 
485  set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}")
486  set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash")
487  set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}")
488 
489  if( NOT DEFINED ${cache_hash_var}
490  OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}")
491  unset(${cache_var} CACHE)
492  endif()
493 
494  if(NOT DEFINED ${cache_var})
495  set(skip_test FALSE)
496 
497  if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR)
498  set(skip_test TRUE)
499  endif()
500 
501  if(skip_test)
502  set(has_dynamic_lookup FALSE)
503  set(link_flags)
504  else()
505  _test_weak_link_project(${target_type}
506  ${lib_type}
507  has_dynamic_lookup
508  link_flags)
509  endif()
510 
511  set(caveat " (when linking ${target_type} against ${lib_type})")
512 
513  set(${cache_var} "${has_dynamic_lookup}"
514  CACHE BOOL
515  "linker supports dynamic lookup for undefined symbols${caveat}")
516  mark_as_advanced(${cache_var})
517 
518  set(${result_var} "${link_flags}"
519  CACHE STRING
520  "linker flags for dynamic lookup${caveat}")
521  mark_as_advanced(${result_var})
522 
523  set(${cache_hash_var} "${cmake_flags_hash}"
524  CACHE INTERNAL "hashed flags for ${cache_var} check")
525  endif()
526 
527  set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE)
528  set(${link_flags_var} "${${result_var}}" PARENT_SCOPE)
529 endfunction()
530 
531 function(mitk_target_link_libraries_with_dynamic_lookup target)
532  _get_target_type(target_type ${target})
533 
534  set(link_props)
535  set(link_items)
536  set(link_libs)
537 
538  foreach(lib ${ARGN})
539  _get_target_type(lib_type ${lib})
540  mitk_check_dynamic_lookup(${target_type}
541  ${lib_type}
542  has_dynamic_lookup
543  dynamic_lookup_flags)
544 
545  if(has_dynamic_lookup)
546  if(dynamic_lookup_flags)
547  if("${target_type}" STREQUAL "EXE")
548  list(APPEND link_items "${dynamic_lookup_flags}")
549  else()
550  list(APPEND link_props "${dynamic_lookup_flags}")
551  endif()
552  endif()
553  else()
554  list(APPEND link_libs "${lib}")
555  endif()
556  endforeach()
557 
558  if(link_props)
559  list(REMOVE_DUPLICATES link_props)
560  endif()
561 
562  if(link_items)
563  list(REMOVE_DUPLICATES link_items)
564  endif()
565 
566  if(link_libs)
567  list(REMOVE_DUPLICATES link_libs)
568  endif()
569 
570  if(link_props)
571  set_target_properties(${target}
572  PROPERTIES LINK_FLAGS "${link_props}")
573  endif()
574 
575  set(links "${link_items}" "${link_libs}")
576  if(links)
577  target_link_libraries(${target} "${links}")
578  endif()
579 endfunction()
580 
581 
int main(int argc, char **argv)
_test_weak_link_project(target_type, lib_type, can_weak_link_var, project_name)
_get_target_type(result_var, target)
const std::string TARGET