51

I would like to have control over the type of the libraries that get found/linked with my binaries in CMake. The final goal is, to generate binaries "as static as possible" that is to link statically against every library that does have a static version available. This is important as would enable portability of binaries across different systems during testing.

ATM this seems to be quite difficult to achieve as the FindXXX.cmake packages, or more precisely the find_library command always picks up the dynamic libraries whenever both static and dynamic are available.

Tips on how to implement this functionality - preferably in an elegant way - would be very welcome!

Jack Kelly
  • 18,264
  • 2
  • 56
  • 81
pszilard
  • 1,942
  • 1
  • 15
  • 18

2 Answers2

29

I did some investigation and although I could not find a satisfying solution to the problem, I did find a half-solution.

The problem of static builds boils down to 3 things:

  1. Building and linking the project's internal libraries.

    Pretty simple, one just has to flip the BUILD_SHARED_LIBS switch OFF.

  2. Finding static versions of external libraries.

    The only way seems to be setting CMAKE_FIND_LIBRARY_SUFFIXES to contain the desired file suffix(es) (it's a priority list).

    This solution is quite a "dirty" one and very much against CMake's cross-platform aspirations. IMHO this should be handled behind the scenes by CMake, but as far as I understood, because of the ".lib" confusion on Windows, it seems that the CMake developers prefer the current implementation.

  3. Linking statically against system libraries.

CMake provides an option LINK_SEARCH_END_STATIC which based on the documentation: "End a link line such that static system libraries are used." One would think, this is it, the problem is solved. However, it seems that the current implementation is not up to the task. If the option is turned on, CMake generates a implicit linker call with an argument list that ends with the options passed to the linker, including -Wl,-Bstatic. However, this is not enough. Only instructing the linker to link statically results in an error, in my case: /usr/bin/ld: cannot find -lgcc_s. What is missing is telling gcc as well that we need static linking through the -static argument which is not generated to the linker call by CMake. I think this is a bug, but I haven't managed to get a confirmation from the developers yet.

Finally, I think all this could and should be done by CMake behind the scenes, after all it's not so complicated, except that it's impossible on Windows - if that count as complicated...

sanmai
  • 29,083
  • 12
  • 64
  • 76
pszilard
  • 1,942
  • 1
  • 15
  • 18
16

A well made FindXXX.cmake file will include something for this. If you look in FindBoost.cmake, you can set the Boost_USE_STATIC_LIBS variable to control whether or not it finds static or shared libraries. Unfortunately, a majority of packages do not implement this.

If a module uses the find_library command (most do), then you can change CMake's behavior through CMAKE_FIND_LIBRARY_SUFFIXES variable. Here's the relevant CMake code from FindBoost.cmake to use this:

IF(WIN32)
    SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
ELSE(WIN32)
    SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
ENDIF(WIN32)

You can either put this before calling find_package, or, better, you can modify the .cmake files themselves and contribute back to the community.

For the .cmake files I use in my project, I keep all of them in their own folder within source control. I did this because I found that having the correct .cmake file for some libraries was inconsistent and keeping my own copy allowed me to make modifications and ensure that everyone who checked out the code would have the same build system files.

Jonathan Sternberg
  • 6,421
  • 7
  • 39
  • 58
  • Thanks for the comment, I've actually came to the conclusion in the meanwhile that the above is the only feasible solution to get the static version of external libs detected. However, this is a quite dirty way, I'd prefer not to have to specify suffixes. Unfortunately, based on discussions on the CMake mailing list it seems that as Windows has a quite confusing library naming there is no plan to implement it properly. This should work on most *NIX system, though. – pszilard Nov 08 '10 at 18:47
  • Note that if you want *only* the static libraries to be detected (and thus perhaps configuration to fail, then the above commands should not prepend the static-library filenames to CMAKE_FIND_LIBRARY_SUFFIXES, but instead set that variable to those filenames. This inhibits find_*() functions from ever picking up shared libraries with the usual suffixes – mabraham Nov 20 '14 at 00:36
  • 1
    Note also that it does not matter how "well made" the FindXXXX.cmake package is if the library is found in an implicit link directory, e.g. system path. CMake deliberately reverts to -lXXXX to permit the linker to use its normal resolution in that case - see http://public.kitware.com/pipermail/cmake/2015-January/059702.html – mabraham Feb 19 '15 at 23:37
  • shouldn't this be: `IF(MSVC)`? – frankelot Apr 17 '20 at 22:43