[libunwind] Try to add --unwindlib=none while configuring and building libunwind
authorMartin Storsjö <martin@martin.st>
Fri, 10 Sep 2021 19:14:48 +0000 (22:14 +0300)
committerMartin Storsjö <martin@martin.st>
Fri, 5 Nov 2021 08:10:19 +0000 (10:10 +0200)
If Clang is set up to link directly against libunwind (via the
--unwindlib option, or the corresponding builtin default option),
configuring libunwind will fail while bootstrapping (before the
initial libunwind is built), because every cmake test will
fail due to -lunwind not being found, and linking the shared library
will fail similarly.

Check if --unwindlib=none is supported, and add it in that case.
Using check_c_compiler_flag on its own doesn't work, because that only
adds the tested flag to the compilation command, and if -lunwind is
missing, the linking step would still fail - instead try adding it
to CMAKE_REQUIRED_FLAGS and restore the variable if it doesn't work.

This avoids having to pass --unwindlib=none while building libunwind.

Differential Revision: https://reviews.llvm.org/D112126

cmake/Modules/CheckLinkerFlag.cmake [new file with mode: 0644]
cmake/Modules/EnableLanguageNolink.cmake [new file with mode: 0644]
libunwind/CMakeLists.txt
libunwind/cmake/config-ix.cmake
libunwind/src/CMakeLists.txt

diff --git a/cmake/Modules/CheckLinkerFlag.cmake b/cmake/Modules/CheckLinkerFlag.cmake
new file mode 100644 (file)
index 0000000..722fe5b
--- /dev/null
@@ -0,0 +1,17 @@
+include(CMakePushCheckState)
+include(CheckCCompilerFlag)
+
+function(llvm_check_linker_flag flag dest)
+  # If testing a flag with check_c_compiler_flag, it gets added to the compile
+  # command only, but not to the linker command in that test. If the flag
+  # is vital for linking to succeed, the test would fail even if it would
+  # have succeeded if it was included on both commands.
+  #
+  # Therefore, try adding the flag to CMAKE_REQUIRED_FLAGS, which gets
+  # added to both compiling and linking commands in the tests.
+
+  cmake_push_check_state()
+  set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag}")
+  check_c_compiler_flag("" ${dest})
+  cmake_pop_check_state()
+endfunction()
diff --git a/cmake/Modules/EnableLanguageNolink.cmake b/cmake/Modules/EnableLanguageNolink.cmake
new file mode 100644 (file)
index 0000000..18668c6
--- /dev/null
@@ -0,0 +1,11 @@
+macro(llvm_enable_language_nolink)
+  # Set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY to disable linking
+  # in the compiler sanity checks. When bootstrapping the toolchain,
+  # the toolchain itself is still incomplete and sanity checks that include
+  # linking may fail.
+  set(__SAVED_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE})
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+  enable_language(${ARGV})
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE ${__SAVED_TRY_COMPILE_TARGET_TYPE})
+  unset(__SAVED_TRY_COMPILE_TARGET_TYPE)
+endmacro()
index f16d49a..bec0d1f 100644 (file)
@@ -23,7 +23,11 @@ set(LIBUNWIND_LIBCXX_PATH "${CMAKE_CURRENT_LIST_DIR}/../libcxx" CACHE PATH
         "Specify path to libc++ source.")
 
 if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR LIBUNWIND_STANDALONE_BUILD)
-  project(libunwind LANGUAGES C CXX ASM)
+  # We may have an incomplete toolchain - do language support tests without
+  # linking.
+  include(EnableLanguageNolink)
+  project(libunwind LANGUAGES NONE)
+  llvm_enable_language_nolink(C CXX ASM)
 
   set(PACKAGE_NAME libunwind)
   set(PACKAGE_VERSION 14.0.0git)
@@ -179,6 +183,14 @@ include(HandleLibunwindFlags)
 # Get required flags.
 add_target_flags_if(LIBUNWIND_BUILD_32_BITS "-m32")
 
+# Compiler tests may be failing if the compiler implicitly links in libunwind,
+# which doesn't exist yet. This gets waived by --unwindlib=none
+# later in config-ix below, but the tests for --target etc before that may
+# be failing due to it. Only test compilation, not linking, for these
+# tests here now.
+set(CMAKE_TRY_COMPILE_TARGET_TYPE_ORIG ${CMAKE_TRY_COMPILE_TARGET_TYPE})
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
 if(LIBUNWIND_TARGET_TRIPLE)
   add_target_flags_if_supported("--target=${LIBUNWIND_TARGET_TRIPLE}")
 endif()
@@ -188,6 +200,7 @@ endif()
 if(LIBUNWIND_SYSROOT)
   add_target_flags_if_supported("--sysroot=${LIBUNWIND_SYSROOT}")
 endif()
+set(CMAKE_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE_ORIG})
 
 # Configure compiler.
 include(config-ix)
index 78f116c..ec10733 100644 (file)
@@ -2,9 +2,18 @@ include(CMakePushCheckState)
 include(CheckCCompilerFlag)
 include(CheckCXXCompilerFlag)
 include(CheckLibraryExists)
+include(CheckLinkerFlag)
 include(CheckSymbolExists)
 include(CheckCSourceCompiles)
 
+# The compiler driver may be implicitly trying to link against libunwind, which
+# might not work if libunwind doesn't exist yet. Try to check if
+# --unwindlib=none is supported, and use that if possible.
+llvm_check_linker_flag("--unwindlib=none" LIBUNWIND_SUPPORTS_UNWINDLIB_NONE_FLAG)
+if (LIBUNWIND_SUPPORTS_UNWINDLIB_NONE_FLAG)
+  set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} --unwindlib=none")
+endif()
+
 check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB)
 
 if (NOT LIBUNWIND_USE_COMPILER_RT)
@@ -25,11 +34,11 @@ endif()
 # required for the link to go through. We remove sanitizers from the
 # configuration checks to avoid spurious link errors.
 
-check_c_compiler_flag(-nostdlib++ LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG)
+llvm_check_linker_flag(-nostdlib++ LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG)
 if (LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG)
   set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nostdlib++")
 else()
-  check_c_compiler_flag(-nodefaultlibs LIBUNWIND_SUPPORTS_NODEFAULTLIBS_FLAG)
+  llvm_check_linker_flag(-nodefaultlibs LIBUNWIND_SUPPORTS_NODEFAULTLIBS_FLAG)
   if (LIBUNWIND_SUPPORTS_NODEFAULTLIBS_FLAG)
     set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nodefaultlibs")
   endif()
index ce3217f..3b20e97 100644 (file)
@@ -83,6 +83,7 @@ if (LIBUNWIND_ENABLE_THREADS)
 endif()
 
 # Setup flags.
+add_link_flags_if(LIBUNWIND_SUPPORTS_UNWINDLIB_NONE_FLAG --unwindlib=none)
 if (LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG)
   add_link_flags_if_supported(-nostdlib++)
 else()