Skip to content
May 1, 2012 / ecrafter

CMake Tutorial Part 4 – Libraries

Library with unit tests

So far our build target has been a single executable. We’ll next try setting up a project with two targets: a library and a test executable for that library.

Here’s the structure:

/part4
    /bin
    /lib
    /part4
        Library.hpp
        Library.cpp
        CMakeLists.txt
    /test
        Test.cpp
        CMakeLists.txt
    CMakeLists.txt
    /build
        /debug
        /release

Before getting into the contents of the files, let’s talk about the desired result of the build. I want the library to be named part4library (libpart4library.so) and I want it located in the /lib folder. I want the test executable to be named part4test and I want it located in the /bin folder. Also, I want the header and source files to be located in a subdirectory named /part4. This might seem confusing, but it arranges things so that when another project includes the Library.hpp header file, the name of the part4 project will be part of the include:

#include "part4/Library.hpp"

This helps to keep it clear where the Library.hpp file is coming from (we’ll see this in action in the next part of the tutorial).

Locating the project executables in a /bin directory, locating the libraries in a /lib directory, and locating the header files in a subdirectory with the name of the project are in keeping with some standard Linux project conventions. Also, I’d like a debug postfix, _d, added to both executable and library when build type is Debug. We’ll be setting CMake scripts to achieve these results.

Here is the source code:
Library.hpp

#pragma once

void print(void);

Library.cpp

#include "Library.hpp"
#include <iostream>

void print(void)
{
   std::cout << "Success!" << std::endl;
}

Test.cpp

#include "Library.hpp"

int main(int argc, char** argv)
{
   print();
   return 0;
}

Looking at our directory structure, note that we now have three CMakeLists.txt files. In general, there will be one such file for each folder in the “source tree” that contains code. The source tree is the tree of directories in which source files are contained. In our case, the root of the source tree is /part4, and it has two branches with source code, /part4/part4 and /part4/test.

Complementing the source tree is the “build tree”, or “binary tree” as CMake documentation calls it. The binary tree contains the intermediate build files set up by CMake. In general, the binary tree mirrors the source tree. In this example, because we are doing an out-of-source build, the root of the binary tree will be either /part4/build/debug or /part4/build/release, depending on the build type. Once we run the CMake scripts, CMake will generate the binary tree within the binary root folder, creating, for example, sub-folders of /part4/build/debug/part4 and /part4/build/debug/test. If we were to do an in-source build, the root of the source and binary trees would be the same, /part4. Locations within the source and binary trees can be found using the CMake location variables.

Our top level CMakeLists.txt in /part4 defines the build project and notifies CMake about subdirectories in the source tree by using the add_subdirectory command. It also uses the include_directories command to indicate that header files in the part4 directory should be accessible to the project. Without this, #include “Library.hpp” in Test.cpp will not be resolved. One other feature is the definition of a global BUILD_POSTFIX variable that both of the target scripts will make use of. Note that BUILD_POSTFIX must be defined before add_subdirectory for the variable to be available when the subdirectory script is run. We use the if command to make BUILD_POSTFIX dependent on the CMAKE_BUILD_TYPE variable.
/part4/CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(Part4Project)
set(CMAKE_CXX_FLAGS "-std=c++0x -Wall")
if (CMAKE_BUILD_TYPE STREQUAL Debug)
    set(BUILD_POSTFIX "_d")
else(CMAKE_BUILD_TYPE STREQUAL Debug)
    set(BUILD_POSTFIX "")
endif(CMAKE_BUILD_TYPE STREQUAL Debug)
include_directories(part4)
add_subdirectory(part4)
add_subdirectory(test)

The CMakeLists.txt in the subdirectories define the library and executable targets:
/part4/part4/CMakeLists.txt

add_library(part4library SHARED Library.cpp)
set_target_properties(part4library PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
set_target_properties(part4library PROPERTIES OUTPUT_NAME part4library${BUILD_POSTFIX})

/part4/test/CMakeLists.txt

add_executable(part4test Test.cpp)
target_link_libraries(part4test part4library)
set_target_properties(part4test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set_target_properties(part4test PROPERTIES OUTPUT_NAME part4test${BUILD_POSTFIX})

We have used target_link_libraries to tell CMake that the part4test target will need to be linked to the Library part4library. Also, we have used set_target_properties to 1) move the targets from the binary tree to the source tree and 2) add the BUILD_POSTFIX to the target names.

One interesting point – note that we used

target_link_libraries(part4test part4library)

and not

target_link_libraries(part4test part4library${BUILD_POSTFIX})

You might think we should use the latter, since when we do a Debug build our library will be named libpart4library_d.so, not libpart4library.so. But it is the former that is correct, as CMake notices that part4library is another target in the same project and automatically follows the renaming with the build postfix. If it were not the name of a target, we would have to specify the correct postfix.

To build the debug project and run the test, we do the following:

cd part4
cd build/debug
cmake ../.. -DCMAKE_BUILD_TYPE=Debug
make
cd ../../bin
./part4test_d
About these ads

5 Comments

Leave a Comment
  1. Mosy / May 6 2013 4:01 pm

    you set test_d to be in bin, not in test

  2. Adnan Munawar / Jul 11 2013 2:39 am

    Hello, thank you for such a good tutorial. I have one question though. I have been following exactly this tutorial and for some reason I have to place my Library.hpp file in the test directory where the source file is. If I don’t do that it says fatal error: Library.hpp: No such file or directory.

    Helpppppp please.

    • Adnan Munawar / Jul 12 2013 3:26 am

      Oh I think I got it. The include_directories(part4) command that you were using, I thought, you were using the name of the root directory of project as part4 and not the part4 directory where libraries are. My project name was gizmo and the place where libraries were was called libs. So that is why I was having the problem. Its such a nice tutorial, thanks again. Just a little suggestion though, that you might want to change the name of the source tree root dir and the other directories to be distinct, so should the name of the project to avoid confusion.

      • ecrafter / Jul 12 2013 5:49 am

        Thanks for the feedback, Adnan. I agree that I’ve overloaded the names too much. I’m in the process of renaming things to make it clearer.

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: