[Documentation] [TitleIndex] [WordIndex

CMakeLists Examples

This page collects examples of using the rosbuild API to perform common tasks.

Many of these examples include standard CMake modules. E.g., the following line pulls in the CheckFunctionExists module, which is often defined in a location like /usr/share/cmake-2.4/Modules/CheckFunctionExists.cmake:

include(CheckFunctionExists)

The standard CMake modules are listed and explained in the CMake documentation.

Building an executable

A simple package that creates a single binary from one source file might look like this:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_executable(bumblebee_bridge bumblebee_bridge.cpp)

The first line tells CMake what version you need. The second line includes the rosbuild.cmake module, which provides some macros to simplify common ROS build operations.

The third and fourth lines invoke two of these macros. rosbuild_init() invokes rospack to retrieve the build flags for this package. This includes getting compile and link flags for all of bumblebee_bridge's dependencies.

The rosbuild_add_executable call says that you want to build an executable called bumblebee_bridge from the source file bumblebee_bridge.cpp (more source files could have listed there). This macro automatically brings in the compile and link flags exported by the dependencies that were declared in this package's manifest.xml. By default, the executable will go in the same directory as the source file(s). Seerosbuild_init() below to change this default.

NOTE: therosbuild_init() macro does NOT bring in the compile or link flags that are exported by the package presently being built. It ONLY brings in the flags exported by the present package's dependencies. The flags in the export section of your manifest.xml are for use by others, not by you. If you need some of your package's exported flags when building the package itself, use the appropriate CMake calls in your CMakeLists.txt, such as include_directories() and target_link_libraries(). See below for examples.

But see Linking against libraries built by other ROS packages for when not to use target_link_libraries().

Building a library

Many ROS packages build a library, like so:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_library(TF src/libTF.cpp src/Pose3DCache.cpp src/Pose3D.cpp)

This file compile a library, called libTF, from the three source files listed. By default, the library will go into the lib directory in your package. See rosbuild_init() below for changing this default.

Building a library and an executable that uses it

To extend the previous example, you may want to build a library, and then use it in building an executable, for example, a test program:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_library(TF src/libTF.cpp src/Pose3DCache.cpp src/Pose3D.cpp)
rosbuild_add_executable(testTF src/test/main.cpp)
target_link_libraries(testTF TF)

This case is similar to the previous one, with the addition of a call t rosbuild_add_executable(), and a call to target_link_libraries(). The latter call is needed to tell CMake that the executable testTF depends on the library libTF.

Building a C/C++ unit test (gtest)

We use Google Test (gtest) for unit testing of C/C++ code. Here's an example that builds a library (image_loader) and then a gtest to test it:

cmake_minimum_required(VERSION 2.4.6 FATAL_ERROR)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_library(image_loader src/image_loader.cpp)
target_link_libraries(image_loader SDL SDL_image)
rosbuild_add_gtest(test/utest test/utest.cpp test/test_constants.cpp)
target_link_libraries(test/utest image_loader SDL SDL_image)

The arguments to rosbuild_add_gtest() are the same as for rosbuild_add_executable(). The difference is that rosbuild_add_gtest() adds the flags needed for compiling and linking against libgtest, and sets up dependencies so that the resulting executable is run during make test on the package. Note that you need to link the gtest executable against the libraries it uses (in this case, image_loader, SDL, and SDL_image), just as you would do for any other executable.

It is sometimes the case that you need to add compile and/or link flags that are not reported b rosbuild_init(), including when linking against libraries that are built within the package. Adding library search paths, libraries to link against, and include search paths is easily handled via standard CMake commands, such as target_link_libraries(), include_directories(), and link_directories().

In general, these commands append their arguments to what came before, so you can use them to add to wha rosbuild_init() already assembled.

To add or remove other link flags (e.g., warnings, optimizations), use rosbuild_add_compile_flags(), rosbuild_remove_compile_flags(), rosbuild_add_link_flags(), and rosbuild_remove_link_flags().

Linking against libraries built by other ROS packages

Do not use target_link_libraries() to link against a library built by another ROS package. Libraries built by other ROS packages should export appropriate link flags that will be added to your package build automatically because of the package dependencies. If you find that your package doesn't build because you're missing a -lfoo argument for some libfoo that's built by another package, go fix the other package, by adding -lfoo to the export line in its manifest.xml.

Linking against an external library

Example: wavefront_player needs to link against libwavefront_standalone:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_executable(wavefront_player wavefront_player.cc)
target_link_libraries(wavefront_player wavefront_standalone)

Read this section for when not to use target_link_libraries().

Linking against a library in your package

Example: rosTF builds the library librosTF, then builds two executables that link against it:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_library(rosTF src/rosTF.cpp)
rosbuild_add_executable(testServer src/testServer.cc)
target_link_libraries(testServer rosTF)
rosbuild_add_executable(testClient src/testClient.cc)
target_link_libraries(testClient rosTF)

Using pkg-config to find an external dependency

Example: map_server needs gdk-pixbuf-2.0, which is not available as a ROS package, so we find it with pkg-config and add the appropriate flags into the build:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_executable(map_server map_server.cc)

include($ENV{ROS_ROOT}/core/rosbuild/FindPkgConfig.cmake)
pkg_check_modules(GDKPIXBUF REQUIRED gdk-pixbuf-2.0)
include_directories(${GDKPIXBUF_INCLUDE_DIRS})
link_directories(${GDKPIXBUF_LIBRARY_DIRS})
target_link_libraries(map_server ${GDKPIXBUF_LIBRARIES})

Note that we include FindPkgConfig.cmake from the ROS tree. This is because it was introduced into the CMake distribution after 2.4.6.

Compiling and linking with profiling

You probably don't want to do this. Prefer the 'callgrind' tool inside 'valgrind' and the visualization tool 'kcachegrind' to gprof and gcc -pg flags.

Example, the default compile line include -fno-strict-aliasing. If you know what you're doing, you may want to remove this flag, to allow strict aliasing:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_executable(wavefront_player wavefront_player.cc)
rosbuild_remove_compile_flags(wavefront_player "-fno-strict-aliasing")

Using Boost

ROS code makes extensive use of boost. While useful and powerful, boost is fantastically difficult to find and use in a portable way. So we provide special macros for gathering build flags to use boost. If you use any boost code, you must call rosbuild_add_boost_directories() to modify the include path to find the boost headers. Further, you must call rosbuild_link_boost() on each target that uses boost. For example:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
rosbuild_add_boost_directories()
rosbuild_add_library(foo src/foo.cpp)
target_link_libraries(foo bar)
rosbuild_link_boost(foo thread signals system)

In this case, the foo library uses boost::thread, boost::signals, and boost::system. It also uses the bar library, which is presumably installed somewhere.

Testing for system capabilities

We strive for ROS to be cross-platform. In some cases, this requires different code for different systems. CMake provides a rich set of tools for testing capabilities, and provides a way for you to condition your code on the results of these tests. Below are some examples; for more information, see the CMake manual.

NOTE: always test for capabilities, not system type. E.g., test for the presence vs. absence of the function clock_gettime(), not whether your running on Windows vs. Linux.

Checking for a function

Let's say we want to use the function trunc(), but we know that it's not available everywhere (note that determining which functions are and are not available everywhere is not easy; best to learn over time). In our CMakeLists.txt, we add these lines:

include(CheckFunctionExists)
# Not everybody has trunc (e.g., Windows, embedded arm-linux)
CHECK_FUNCTION_EXISTS(trunc HAVE_TRUNC)
# Output test results to config.h
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)

The first line pulls in the standard CMake modules that provides function-checking. The second line invokes this check, putting the result in the CMake variable HAVE_TRUNC. Now, we want to get that value into our code. The third line reads in a template header called config.h.in and does replacements on it to produce a header config.h that can be #include'd in our C code.

The config.h.in looks like this:

#cmakedefine HAVE_TRUNC

If the system has trunc(), then the resulting config.h will look like this:

#define HAVE_TRUNC

If the system doesn't have trunc(), you'd get this:

/* #undef HAVE_TRUNC */

In either case, you can then do this in your code that needs to behave differently:

#include <config.h>
...
#ifdef HAVE_TRUNC
  <use trunc() as usual>
#else
  <work around the fact that trunc() isn't available>
#endif

Checking for a header

A slight variant on the previous example is to test for a header. The relevant CMakeLists.txt content:

include(CheckIncludeFiles)
# Not everybody has <ifaddrs.h> (e.g., embedded arm-linux)
CHECK_INCLUDE_FILES(ifaddrs.h HAVE_IFADDRS_H)
# Output test results to config.h
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)

The rest is the same as before.

When something critical is missing

It can happen that you must have some capability that isn't available everywhere. For example, a program that uses the Linux joystick interface needs the header <linux/joystick.h>. Without it, this program can't be compiled.

One strategy is to simply #include the header, and let the build break when it's not around. This is not very informative or nice to the user.

A better strategy is to test for the header and tell the user what the problem is. E.g.,:

# Must have <linux/joystick.h>
include(CheckIncludeFiles)
check_include_files(linux/joystick.h HAVE_LINUX_JOYSTICK_H)
if(NOT HAVE_LINUX_JOYSTICK_H)
  message(FATAL_ERROR "Can't proceed without <linux/joystick.h>")
endif(NOT HAVE_LINUX_JOYSTICK_H)

In this case, we don't create a config.h, because we're not going to use the result of the test in our code (the header is still unconditionally #included). The call to message() with the FATAL_ERROR option prints the message to the screen and stops the build.

Architecture-specific build flags

One exception to the above-mentioned prohibition against checking for system type is when you want to supply processor-specific build flags for the purpose of optimization. Use the builtin CMakeDetermineSystem module. E.g.:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
# Include the module
include(CMakeDetermineSystem)
rosbuild_init()
rosbuild_add_library(starfeature src/detector.cpp src/integral.cpp src/keypoint.cpp)
# Add processor-independent flags
rosbuild_add_compile_flags(starfeature -save-temps)
# Check for processor type and add appropriate flags
if(CMAKE_SYSTEM_PROCESSOR MATCHES "i686")
 '''`rosbuild_add_compile_flags(starfeature`''' -march=pentium3 -msse3)
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686")

In addition to CMAKE_SYSTEM_PROCESSOR, you might want to check CMAKE_SYSTEM_NAME and CMAKE_SYSTEM_VERSION.

Sources in multiple directories

If you like, you can keep your package's source files in separate subdirectories within the package directory. In this situation, you should keep the basic ROS/CMake setup the same in the top-level CMakeLists.txt, with the addition of the add_subdirectory() command:

cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
rosbuild_init()
set(EXECUTABLE_OUTPUT_PATH ${roscpp_tutorials_SOURCE_DIR}/bin)
add_subdirectory(src)

The src/CMakeLists.txt might in turn refer to other directories:

add_subdirectory(talker)
add_subdirectory(listener)
add_subdirectory(case_flip)
add_subdirectory(case_flip_client)
add_subdirectory(notify_connect)

When you reach a directory where you want to build something, you've inherited the build setup from above, so you can just declare what you want to build (src/talker/CMakeLists.txt):

rosbuild_add_executable(talker talker.cpp)

2024-11-16 17:43