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
March 28, 2010 at 8:53 pm |
This is the best CMake mini-tutorial I have seen after ~8 hours of Google searching. Thanks!
March 28, 2010 at 11:03 pm |
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!
August 9, 2010 at 3:47 am |
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!
September 24, 2010 at 9:51 am |
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.
January 12, 2011 at 10:02 pm |
Thanks….
April 1, 2011 at 10:39 pm |
Thanks so much! Have to agree with the others in that this the only tutorial which delves into this topic. Saved me hours !
April 19, 2011 at 10:47 am |
Awesome. I agree with the previous posters and a thousand thanks for saving me so much time 😀
May 26, 2011 at 7:30 pm |
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?
May 26, 2011 at 7:46 pm |
If I recall correctly you should use find_library() as explained above to find the .so plus include_directories() to find the .h
May 26, 2011 at 7:58 pm |
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?
May 26, 2011 at 10:55 pm |
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})
July 7, 2011 at 4:07 pm |
@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
July 11, 2011 at 11:30 am
Good catch.
And yes, I think there’s lack of documentation about CMake too.
October 3, 2011 at 11:50 am |
It helped me..Thanks a lot!!!
December 6, 2011 at 7:54 pm |
Have you had any luck creating microcontroller projects using cmake?
December 7, 2011 at 1:42 am |
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.
July 10, 2012 at 3:11 pm |
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.
February 25, 2013 at 4:19 pm |
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
July 25, 2013 at 3:00 pm |
It helps. Thanks
July 7, 2014 at 1:03 pm |
good oneee
June 22, 2015 at 4:05 pm |
Finally solved my problem. Thanks