28

Is it possible to tell cmake to link against a static library instead of shared?

At the top of my CMakeLists.txt I have the following configured:

set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})

Later, I add a binary, and tell it to link against tcmalloc in release mode:

target_link_libraries(${BIN_NAME} optimized tcmalloc_minimal)

The resulting makefile links aginst the shared version of tcmalloc:

$ make VERBOSE=1 | grep tcmalloc
/usr/bin/c++ ... -Wl,-Bdynamic ltcmalloc_minimal 

Further proof:

$ ldd app 
    ...
    libtcmalloc_minimal.so.4 => /usr/local/lib/libtcmalloc_minimal.so.4 (0x00007eff89733000)
    ...

Both static and shared versions of tcmalloc exist:

$ ls -1 /usr/local/lib/libtcmalloc_minimal*
/usr/local/lib/libtcmalloc_minimal.a
/usr/local/lib/libtcmalloc_minimal_debug.a
/usr/local/lib/libtcmalloc_minimal_debug.la
/usr/local/lib/libtcmalloc_minimal_debug.so
/usr/local/lib/libtcmalloc_minimal_debug.so.4
/usr/local/lib/libtcmalloc_minimal_debug.so.4.2.6
/usr/local/lib/libtcmalloc_minimal.la
/usr/local/lib/libtcmalloc_minimal.so
/usr/local/lib/libtcmalloc_minimal.so.4
/usr/local/lib/libtcmalloc_minimal.so.4.2.6

Question:

How can I configure cmake to link against the static version of tcmalloc?

Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213

3 Answers3

8

You can create a helper function which sets CMAKE_FIND_LIBRARY_SUFFIXES at function scope (so therefore doesn't affect the parent scope) which searches for the library in question and sets an output variable with the result

function(find_static_library LIB_NAME OUT)

    if (WIN32 OR MSVC)
        set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
    elseif (UNIX)
        set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
    endif()      

    find_library(
        FOUND_${LIB_NAME}_STATIC
        ${LIB_NAME}
        )

    if (FOUND_${LIB_NAME}_STATIC)
        get_filename_component(ABS_FILE ${FOUND_${LIB_NAME}_STATIC} ABSOLUTE)
    else()
        message(SEND_ERROR "Unable to find library ${LIB_NAME}")
    endif()

    set(${OUT} ${ABS_FILE} PARENT_SCOPE)

endfunction()

You can then call this function from somewhere in your CMakeLists.txt to populate a variable with the location of the library.

Failure to find it results in a hard failure

find_static_library(tcmalloc_minimal TCMALLOC)

You can then use this variable in your call to target_link_libraries and be sure you're linking against the static version

target_link_libraries(${BIN_NAME} optimized ${TCMALLOC})

Here you can see the result:

$ make VERBOSE=1 | grep tcmalloc
/usr/bin/c++ ... /usr/local/lib/libtcmalloc_minimal.a ...
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • 1
    you need to add ` unset(FOUND CACHE)` before the find_library call or else the second find_static_library won't work. – over_optimistic Aug 13 '16 at 21:31
  • 1
    @over_optimistic very late reply, but I updated the answer to undo the need for the `unset` – Steve Lorimer Sep 03 '19 at 14:48
  • This is a minor nit-pick of a great answer, but it's awkward that you chose a different argument order than [cmake's built-in `find_library()` command](https://cmake.org/cmake/help/latest/command/find_library.html). The built-in order is `find_library(VAR name)`, but the function above is `find_static_library(name VAR)`. If you fix the order, that would help those of us lazy programmers who blindly find-and-replace. – Stuart Berg Dec 26 '21 at 15:28
  • This does not work on OSX –  Feb 09 '22 at 23:23
5

If you only need to support non-Windows platforms, then this old email from the CMake mailing list from one of the Kitware developers gives the simplest method. In essence, use find_library() to find the location of the actual library, favouring static libraries over shared ones by listing them first in the names to look for. i.e.

find_library(TCMALLOC_LIB NAMES libtcmalloc_minimal.a tcmalloc_minimal)

You would then link to the library found in the usual way:

target_link_libraries(${BIN_NAME} ${TCMALLOC_LIB})

You could get smarter about how you define the static library name if you need to support platforms where a static library is named something other than lib???.a. You would use CMAKE_STATIC_LIBRARY_PREFIX and CMAKE_STATIC_LIBRARY_SUFFIX variables for that.

On Windows, the problem is that you cannot distinguish between a static library and the import library for a DLL, as discussed in this old issue in the Kitware bug tracker. Both have the file extension .lib, so you can't use the extension to work out if a particular file is a static library or not, unlike Unix-based platforms where you can.

Craig Scott
  • 9,238
  • 5
  • 56
  • 85
0

You have to set your CMAKE_FIND_LIBRARY_SUFFIXES variable in this manner:

set(CMAKE_FIND_LIBRARY_SUFFIXES .a)

because in default CMAKE_FIND_LIBRARY_SUFFIXES there is also .so suffix (and it seems not searching in order of insertion). In order to allow portability other suffixes should be added (see here for default values of CMAKE_FIND_LIBRARY_SUFFIXES on different platforms).

Community
  • 1
  • 1
Francesco Argese
  • 626
  • 4
  • 11
  • 2
    That didn't work - I did just what you suggested, and it still links against the shared lib. According to the [docs](https://cmake.org/cmake/help/v3.0/variable/CMAKE_FIND_LIBRARY_SUFFIXES.html) that setting is only for `find_library` not for `target_link_libraries` – Steve Lorimer Apr 20 '16 at 20:30
  • Yes, it requires using find_library. Usually I use find_library to find external libraries and store them in CMake variables; then I add them to target libraries using variables. – Francesco Argese Apr 20 '16 at 20:37
  • 1
    Something like this: http://stackoverflow.com/questions/14243524/cmake-find-library-matching-behavior. Then you need to add the library var to TARGET_LINK_LIBRARIES with ${VAR_LIBRARY_NAME}.If you have problems try to print variable obtained through find_library with message(STATUS ...) to understand if it has found the right library. – Francesco Argese Apr 20 '16 at 20:50