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

3

13

With some compilers, using pow and certain other functions in a C program requires linking to the m library. However, some compilers don't require this and would error out on linking to the m library. An almost identical situation exists for C++ with std::thread and pthread, but the CMake module FindThreads alleviates this entirely - is there some similar module for libm?

What is the best way to detect what to do with CMake? This is my current solution, which is less than ideal because there are many more C compilers than just GCC and MSVC:

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

This works for my purposes but I'm pretty sure there are cases where it would fail and require manual user intervention, which isn't fun for someone who doesn't know about this obscurity. Ideally I don't want the user to have to specify whether their compiler is weird or not via the commandline; I want to detect it automatically within CMake, since this is the entire point of CMake.

Aube answered 28/9, 2015 at 6:9 Comment(3)
"some compilers don't require this and would error out on linking to the m library" Which one, please?Disquisition
@Disquisition I have worked with an obscure C compiler for a robot microcontroller which I know for a fact does not support -lm because it was the first error I ever got from that compiler. It is not unlikely that my particular code will be used in such a scenario.Aube
@Disquisition I get a link error when libm is set using CMake's target_link_libraries(MY_TARGET m) when using the Visual Studio compiler v16.Hertfordshire
T
12

You should use the CHECK_FUNCTION_EXISTS command to check if pow can be used without additional flags. If this check fails, you can add m library to CMAKE_REQUIRED_LIBRARIES variable, assuming that linking against libm is what's missing. But you'll need to CHECK_FUNCTION_EXISTS again to make sure the linking is sufficient.

Sample code:

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()
Tenderfoot answered 28/9, 2015 at 10:29 Comment(7)
Although this looks like it should work, CHECK_FUNCTION_EXISTS seems to cache the result so that the second time you call it is a no-op.Aube
Oh, right. Either unset(RESULT) or use different variable name in second call.Tenderfoot
Thanks, that worked! I'll accept your answer now, just don't forget t edit it with the fix.Aube
RESULT os am awful variable name. And you should not reuse a variable name. Ergo you don't need the ugly unset with sane variable name choices in the first place.Diastasis
@Diastasis You are right and i've already commented on that. It isn't a production code, but an example.Tenderfoot
Edited to reflect my experience with this code snippet - I needed to clear the value from the cache; also, using more pertinent variable name.Omer
@usr1234567: Edited to change that.Omer
R
5

If I understand correctly, linking libm is always preferred if it is exists.

So, CheckLibraryExists works.

CMakeLists.txt

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

...
target_link_libraries(${PROJECT_NAME} PUBLIC ${POW_LIBS})

tested with Linux x86_64, glibc 2.23 cmake 3.13.2

Ranzini answered 15/2, 2019 at 4:2 Comment(0)
F
4

Common way for check, whether some code is correct for the compiler, is 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})

Cache entry POW_LIBS contains libraries needed for use pow function.

Futurism answered 28/9, 2015 at 10:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.