瀏覽代碼

Fix linking of VTKCoreCppTest by adding a check for linker capabilities

This commit is based on the work of Sankhesh Jhaveri and Bill Hoffman. It
has been adapted to (1) be re-usable by project depending on CTK and
(2) execute the try_compile test only if needed.

It checks whether the linker will be able to resolve all symbols. If the
check fails, another test takes place to make sure the linker is not
lazy-linking. If it is, the flag --no-as-needed is added to
CMAKE_EXE_LINKER_FLAGS.

The link error was:

// ---------------
Linking CXX executable ../../../../../../bin/CTKVisualizationVTKCoreCppTests
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyNumber_Divmod'
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyDict_SetItem'
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyExc_ValueError'
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyNumber_And'
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyDict_SetItemString'
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyNumber_Absolute'
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyExc_OverflowError'
/home/jchris/Projects/Slicer-1-SuperBuild-Release-Qt485/VTK-build/bin/libvtkPythonCore.so.5.10.1: undefined reference to `PyType_IsSubtype'
[...]
// ---------------

While working on Slicer bug #2321, Sankhesh and Bill figured out the
solution:

"Okay, finally with some awesome help from Bill Hoffman we could figure
out what was the root cause of this problem.

Turns out, the shared library libvtkPythonCore.so uses python symbols
but is not linked to libpython.so [1].
Moreover, the test in question - qSlicerDataModuleWidgetsCxxTest does not
reference any python symbols directly. This makes the linker "not link"
the executable to libpython. However, since libvtkPythonCore depends on
libpython without a direct link between the two, we have to suffer with
this bug. :-(

In essence, if there is a shared library "A" that depends on a shared
library "B" but is not linked directly to it and an executable "C"
depends only on shared library "A", linker fails to link to library "B"
and cannot resolve symbols in "A".

Now, you would expect the linker to resolve the dependency up the
toolchain and link the two shared libraries together. This would have
made it much easier. But, Ubuntu package maintainers decided to add a
linker flag "--as-needed" [2] to the default gcc toolchain starting
Ubuntu 12.04.
There is some discussion on this [3].

[1] https://github.com/Kitware/VTK/blob/v5.10.0/Wrapping/Python/CMakeLists.txt#L50
[2] http://linux.die.net/man/1/ld
[3] http://stackoverflow.com/questions/12693624/references-to-fftw-not-resolved-when-linking-with-as-needed
"
Source: http://na-mic.org/Mantis/view.php?id=2321#c6403

Co-authored-by: Sankhesh Jhaveri <sankhesh.jhaveri@kitware.com>
Co-authored-by: Bill Hoffman <bill.hoffman@kitware.com>
Jean-Christophe Fillion-Robin 11 年之前
父節點
當前提交
52551d4eb3

+ 60 - 0
CMake/ctkLinkerAsNeededFlagCheck.cmake

@@ -0,0 +1,60 @@
+###########################################################################
+#
+#  Library:   CTK
+#
+#  Copyright (c) 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.txt
+#
+#  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.
+#
+###########################################################################
+
+#
+# Linker --as-needed flag check
+#
+# Check if the linker will resolve symbols of underlinked libraries
+#
+# This script set the variable CTK_LINKER_NO_AS_NEEDED_FLAG_REQUIRED
+# to either TRUE or FALSE.
+#
+
+if(NOT DEFINED CTK_LINKER_NO_AS_NEEDED_FLAG_REQUIRED)
+  message(STATUS "Checking if --no-as-needed linker flag is required")
+  set(LINK_TEST_SOURCE_DIR ${CTK_CMAKE_DIR}/ctkLinkerAsNeededFlagCheck)
+  set(LINK_TEST_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/ctkLinkerAsNeededFlagCheck)
+  execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${LINK_TEST_BINARY_DIR})
+  try_compile(CTK_LINKER_LINKS_UNDERLINKED_LIBS
+    ${LINK_TEST_BINARY_DIR}
+    ${LINK_TEST_SOURCE_DIR}
+    LINK_TEST
+    )
+  if(CTK_LINKER_LINKS_UNDERLINKED_LIBS)
+    set(CTK_LINKER_NO_AS_NEEDED_FLAG_REQUIRED FALSE CACHE INTERNAL "Test CTK_LINKER_NO_AS_NEEDED_FLAG_REQUIRED")
+    message(STATUS "Checking if --no-as-needed linker flag is required - no")
+  else()
+    try_compile(CTK_LINKER_NO_AS_NEEDED_LINKS_UNDERLINKED_LIBS
+      ${LINK_TEST_BINARY_DIR}
+      ${LINK_TEST_SOURCE_DIR}
+      LINK_TEST_FLAGS
+      CMAKE_FLAGS -DCMAKE_EXE_LINKER_FLAGS=-Wl,--no-as-needed
+      )
+    if(CTK_LINKER_NO_AS_NEEDED_LINKS_UNDERLINKED_LIBS)
+      set(CTK_LINKER_NO_AS_NEEDED_FLAG_REQUIRED TRUE CACHE INTERNAL "Test CTK_LINKER_NO_AS_NEEDED_FLAG_REQUIRED")
+      message(STATUS "Checking if --no-as-needed linker flag is required - yes")
+    else()
+      message(STATUS "Checking if --no-as-needed linker flag is required - failed.")
+      message(WARNING "Could not compile test code."
+        "Linker could fail trying to resolve symbols for underlinked libraries."
+        "See issue 2321 (http://na-mic.org/Mantis/view.php?id=2321) for more details.")
+    endif()
+  endif()
+endif()

+ 5 - 0
CMake/ctkLinkerAsNeededFlagCheck/A.cpp

@@ -0,0 +1,5 @@
+int B();
+int A()
+{
+  return B();
+}

+ 4 - 0
CMake/ctkLinkerAsNeededFlagCheck/B.cpp

@@ -0,0 +1,4 @@
+int B()
+{
+  return 10;
+}

+ 5 - 0
CMake/ctkLinkerAsNeededFlagCheck/C.cpp

@@ -0,0 +1,5 @@
+int A();
+int main()
+{
+  return A();
+}

+ 4 - 0
CMake/ctkLinkerAsNeededFlagCheck/CMakeLists.txt

@@ -0,0 +1,4 @@
+add_library(A SHARED A.cpp)
+add_library(B SHARED B.cpp)
+add_executable(C C.cpp)
+target_link_libraries(C B A)

+ 22 - 0
CMakeLists.txt

@@ -199,10 +199,20 @@ endforeach()
 
 foreach(file
   Libs/ctkExport.h.in
+  CMake/ctkLinkerAsNeededFlagCheck.cmake
   )
   install(FILES ${file} DESTINATION ${CTK_INSTALL_CMAKE_DIR} COMPONENT Development)
 endforeach()
 
+install(FILES
+  CMake/ctkLinkerAsNeededFlagCheck/CMakeLists.txt
+  CMake/ctkLinkerAsNeededFlagCheck/A.cpp
+  CMake/ctkLinkerAsNeededFlagCheck/B.cpp
+  CMake/ctkLinkerAsNeededFlagCheck/C.cpp
+  DESTINATION ${CTK_INSTALL_CMAKE_DIR}/ctkLinkerAsNeededFlagCheck
+  COMPONENT Development
+  )
+
 #-----------------------------------------------------------------------------
 # Testing
 #
@@ -329,6 +339,18 @@ if(MSVC)
 endif()
 
 #-----------------------------------------------------------------------------
+# Check if the linker will resolve symbols of underlinked libraries
+#
+if(UNIX AND NOT APPLE)
+  include(ctkLinkerAsNeededFlagCheck)
+  if(CTK_LINKER_NO_AS_NEEDED_FLAG_REQUIRED)
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-as-needed"
+      CACHE STRING "Flags used by the linker"
+      FORCE)
+  endif()
+endif()
+
+#-----------------------------------------------------------------------------
 # QT
 #
 ctkMacroSetupQt()