CMake part 3: Finding libraries

Over time, I find myself more and more interested in CMake. That’s because I write many little programs, and those that use makefiles routinely break as I switch OS.

The last one was a program that depended on libpng. When I tried to compile it on Mac OS X, it failed to find the library. That’s because on Linux libpng.so is simply in /usr/lib and png.h is in /usr/include, both of which are in the compiler’s search path, so that all you need to do is add “-lpng” when linking.

But on a Mac libpng.dylib (yes, shared libraries on a Mac have .dylib extension) is in /usr/X11/lib and png.h is in /usr/X11/include that are not in the compiler’s search path, so that when compiling you need to add “-I/usr/X11/include” and when linking “-L/usr/X11/lib -lpng”.

The solution was not to keep two separate makefiles, but rather to throw away the makefile and replace it with a CMakeLists.txt

Which brings us to the question: how to link with libraries using CMake?

There are two ways, the first is the find_package command, the other is the find_library command.

Let’s start with find_package. CMake “knows” about many widely used libraries. For them, there is a script to find them in all supported platforms. So, to use a library all you need to do is find it with the find_package command. Here is a simple example of a program that uses threads and so depends on “-lpthread”, main.cpp:

#include <iostream>

using namespace std;

void *thread(void *argv)
{
    cout<<"Into a spawned thread"<<endl;
}

int main()
{
    pthread_t t;
    pthread_create(&t,NULL,thread,NULL);
    pthread_join(t,NULL);
    cout<<"Back in main thread"<<endl;
}

and here is the CMakeLists.txt file:

cmake_minimum_required(VERSION 2.6)
project(TEST)

## Target
set(TEST_SRCS main.cpp)
add_executable(test ${TEST_SRCS})

## Link libraries
find_package(Threads REQUIRED)
target_link_libraries(test ${CMAKE_THREAD_LIBS_INIT})

As can be seen, the first parameter passed to find_package is the name of the package, the second is “REQUIRED” and means that if the library could not be found, CMake should stop and print an error message.

Once the library is found, you have to say which executable needs it (because a single CMakeLists.txt can be used to produce many executable by just using more add_executable commands). This is achieved with the target_link_libraries command that appends a library to the list of libraries an executable needs. The first parameter is the executable name, the second is the library. Note that find_library generates a variable that contains the name of the library, in this case the name is CMAKE_THREAD_LIBS_INIT. This strange name is an exception, usually all find_package scripts create a variable with the name <libraryname>_LIBRARY.

Now a more complex example: the libpng issue I talked about earlier. It is more complex because you don’t just need to add a library when linking, you also need to tell the compiler where is the png.h file when compiling. Luckily, CMake has a package for libpng that does all that, and here is the CMakeLists.txt example:

cmake_minimum_required(VERSION 2.6)
project(TEST)

## Targets
set(TEST_SRCS test.cpp)
add_executable(test ${TEST_SRCS})

## Link libraries
find_package(PNG REQUIRED)
include_directories(${PNG_INCLUDE_DIR})
target_link_libraries(test ${PNG_LIBRARY})

The find_package command finds the PNG library, target_link_libraries adds the library to the list of libraries the executable needs, and include_directories adds the directory where the .h file is when compiling.

But this isn’t the end. Other than libraries there are collections of libraries. And CMake supports them too. One example are the boost libraries. There is no single libboost.so to link to; instead every sub-library has its .so file. So there should be a way to link only with the desired sub-libraries. This is an example CMakeLists.txt that does that:

cmake_minimum_required(VERSION 2.6)
project(TEST)

## Target
set(TEST_SRCS main.cpp)
add_executable(test ${TEST_SRCS})

## Link libraries
set(BOOST_LIBS thread date_time system)
find_package(Boost COMPONENTS ${BOOST_LIBS} REQUIRED)
target_link_libraries(test ${Boost_LIBRARIES})
find_package(Threads REQUIRED)
target_link_libraries(test ${CMAKE_THREAD_LIBS_INIT})

In this case we initialize a variable with the sub-libraries we want (boost.thread, boost.date_time and boost.system). Then we call find_package with the library name (Boost), the word COMPONENTS followed by the list of sub-libraries and as usual the REQUIRED word. Since boost.thread depends on the system’s thread library, we also use another find_package command to link with threads.

This ends the examples of find_package, but there is one last issue: what if we need a library for which there isn’t a package script? The solution is to use the find_library command. It will search in the system paths for the needed library. Here is an example that uses the command to find the Poco libraries:

cmake_minimum_required(VERSION 2.6)
project(TEST)

## Target
set(TEST_SRCS main.cpp)
add_executable(test ${TEST_SRCS})

## Link libraries
find_library(POCO_FOUNDATION PocoFoundation)
find_library(POCO_NET PocoNet)
target_link_libraries(test ${POCO_FOUNDATION} ${POCO_NET})
find_package(Threads REQUIRED)
target_link_libraries(test ${CMAKE_THREAD_LIBS_INIT})

The find_library command takes two parameters, the first is the variable where the found library will be stored, and the second is the library name (the name is camelcase in this example because Poco libraries are camelcase, the library name is really libPocoFoundation.so).
References: CMake wiki

Advertisements

Tags: ,

21 Responses to “CMake part 3: Finding libraries”

  1. Rune Says:

    This is the best CMake mini-tutorial I have seen after ~8 hours of Google searching. Thanks!

  2. fedetft Says:

    Well, thanks for your comment 🙂
    I’m still learning CMake, and I’ll expand the tutorial as I find other tricks. It’s nice to know its somewhat useful!

  3. spirit8 Says:

    wow, very nice tutorial!, I was trying to set boost libraries in my project four hours and only with this tutorial i accomplished that. Thanks a lot!

  4. algal Says:

    I agree this is the best explanation of CMake basics I have seen after hours of googling.

    And that’s kind of a shame, no? It says something about the system’s usability that their own FAQ, Documentation, and even Google videos are so unhelpful. Just pages of touting advanced features that don’t help you with the basics.

    Maybe the only way to understand this system is to buy their book.

  5. හමා Says:

    Thanks….

  6. Abishek Says:

    Thanks so much! Have to agree with the others in that this the only tutorial which delves into this topic. Saved me hours !

  7. wuchter Says:

    Awesome. I agree with the previous posters and a thousand thanks for saving me so much time 😀

  8. jsin Says:

    Thanks for this post – it is very informative. I still have a small question if anyone can help. Lets say that I have a test_lib.so file that is in /home/libs/ from which I want to include a header in my executable. What do I do?

  9. fedetft Says:

    If I recall correctly you should use find_library() as explained above to find the .so plus include_directories() to find the .h

  10. jsin Says:

    Thanks fedetft.

    include_directories(/home/path_where_header_resides) works fine to find the .h file, but only if the actual .h file is there. If only the .so file is there, the compiler states that it cannot find it.

    I have no idea what the arguments to find_library() should be for linking. If the filename is “test.so”, what arguments should I use?

  11. fedetft Says:

    If I understood correctly your problem, you have two files
    /home/libs/test.h
    /home/libs/test.so
    that you want to link to, but find_library() doesn’t look by default in /home/libs. Is this right? If it is try something like

    include_directories(/home/libs)
    find_library(TEST_LIB NAMES test HINTS /home/libs)
    target_link_libraries(my_executable ${TEST_LIB})

    • Richard Kaufman Says:

      @fedetft

      In find_library( … ), you shouldn’t use HINTS in that case. PATHS is a better option. The documentation mentions that

      “Search the paths specified by the HINTS option. These should be paths computed by system introspection, such as a hint provided by the location of another item already found. Hard-coded guesses should be specified with the PATHS option.” [1]

      So I suggest:
      find_library(TEST_LIB NAMES test PATH /home/libs)

      However, great article! Definitely one of the bests on the web. For some reason I find that there’s a lack of CMake tutorials on the web. Thank you for making this!

      [1] http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_library

  12. Gaurav Says:

    It helped me..Thanks a lot!!!

  13. lbuchy Says:

    Have you had any luck creating microcontroller projects using cmake?

    • fedetft Says:

      Actually, I’ve tried around one year ago, but CMake does not seem to work well with assembler as it does with C and C++, and often the startup code (the one executed before main) is written in asm.
      Also, all of its library searching functionalities are useless when cross compiling, so in the end I’ve reached the conclusion that it’s not such a good idea as it first seems, and continued using plain makefiles for microcontrollers.
      But if your experience is different, let me know, I’m curious.

  14. Hindol Says:

    I just started learning cmake today and I have to admit this is the most useful tutorial I have found so far. Really cleared my doubts about find_package and find_library. Many thanks.

  15. Neha Says:

    I want to include a library in my project and I have written a .cmake file for it but I want to place that .cmake file in the same directory as my project in, So i did this
    find_package(Mylibrary PATHS ${CMAKE_SOURCE_DIR})
    but its not able to detect it

  16. Signal Says:

    It helps. Thanks

  17. Anonymous Says:

    good oneee

  18. animel Says:

    Finally solved my problem. Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: