在 C 中使用 pow 时,CMake 能否检测到我是否需要 link 到 libm?

Can CMake detect if I need to link to libm when using pow in C?

对于某些编译器,using pow and certain other functions in a C program requires linking to the m library。然而,一些编译器不需要这个并且会在链接到 m 库时出错。 std::threadpthread 的 C++ 存在几乎相同的情况,但 CMake 模块 FindThreads 完全缓解了这种情况 - libm 是否有类似的模块?

检测使用 CMake 做什么的最佳方法是什么?这是我目前的解决方案,不太理想,因为 C 编译器比 GCC 和 MSVC 多得多:

if(NOT MSVC)
    target_link_libraries(my-c-target PUBLIC m)
endif()

这适用于我的目的,但我很确定在某些情况下它会失败并需要用户手动干预,这对于不了解这种晦涩难懂的人来说并不有趣。理想情况下,我不希望用户必须通过命令行指定他们的编译器是否为 weird;我想在 CMake 中自动检测它,因为这是 CMake 的全部要点。

检查某些代码对编译器是否正确的常用方法是try_compile

use_pow.c:

#include <math.h>
int main(void) {return pow(2, 2.5);}

CMakeLists.txt:

...
if(NOT DEFINED POW_LIBS)
    try_compile(pow_use_m # RESULT_VAR
                check_pow # bindir
                use_pow.c # srcfile
                LINK_LIBRARIES m)
    if(pow_use_m)
        set(POW_LIBS m CACHE INTERNAL "Libraries for use pow")
    else()
        set(POW_LIBS "" CACHE INTERNAL "Libraries for use pow")
    endif()
endif()

...
target_link_libraries(my-c-target PUBLIC ${POW_LIBS})

缓存条目 POW_LIBS 包含使用 pow 功能所需的库。

您应该使用 CHECK_FUNCTION_EXISTS 命令来检查 pow 是否可以在没有附加标志的情况下使用。如果此检查失败,您可以将 m 库添加到 CMAKE_REQUIRED_LIBRARIES 变量,假设缺少针对 libm 的链接。但是您需要再次 CHECK_FUNCTION_EXISTS 以确保链接足够。

示例代码:

include(CheckFunctionExists)

if(NOT POW_FUNCTION_EXISTS AND NOT NEED_LINKING_AGAINST_LIBM)
  CHECK_FUNCTION_EXISTS(pow POW_FUNCTION_EXISTS)
  if(NOT POW_FUNCTION_EXISTS)
      unset(POW_FUNCTION_EXISTS CACHE)
      list(APPEND CMAKE_REQUIRED_LIBRARIES m)
      CHECK_FUNCTION_EXISTS(pow POW_FUNCTION_EXISTS)
      if(POW_FUNCTION_EXISTS)
          set(NEED_LINKING_AGAINST_LIBM True CACHE BOOL "" FORCE)
      else()
          message(FATAL_ERROR "Failed making the pow() function available")
      endif()
  endif()
endif()

if (NEED_LINKING_AGAINST_LIBM)
     target_link_libraries(your_target_here m)
endif()

如果我理解正确,链接 libm 总是首选(如果存在)。

所以,CheckLibraryExists 有效。

CMakeLists.txt

set(POW_LIBS "")
include(CheckLibraryExists)
check_library_exists(m pow "" LIBM)
if(LIBM)
    list(APPEND POW_LIBS "m")
endif()

...
target_link_libraries(my-c-target PUBLIC ${POW_LIBS})

使用 Linux x86_64、glibc 2.23 cmake 3.13.2

进行测试