Using Player and CMake

A brief overview of how CMake works. More...

A brief overview of how CMake works.

Table of contents:

CMake is a tool designed to help developers get rid of the challenges associated with writing Makefiles in projects. CMake is designed to be portable across many types of systems, and can generate native build scripts for many different compilers on several different operating systems. We will be giving a cursory overview of the CMake and explain how you can use it with your own client applications.

Directory structure

A project directory typically contains the following subdirectories:

Player has a slightly different (and larger directory structure), which works with generally the same principles.

Configuration files

CMake uses various CMakeLists.txt files, usually one in each directory of the project. These files contain logic to set build options, resolve external project dependencies, and add executable and library targets. Custom targets and macros can also be defined and included into the CMake files.

A brief example

Sometimes the best way to explain a topic is by showing an example. Here, we will be building a project that uses CMake to build a c++ client application. All of the code for this example is include in examples/tutorials/cmake

Base directory

CMake requires at least one CMakeLists.txt file for project configuration. Generally, projects distribute the configuration, and place a CMakeLists.txt in each subdirectory of the source tree as well. This allows you to keep things modular: the base directory CMakeLists.txt can handle setting global configuration (project name, version, project dependency discovery), and the subdirectory files can handle.

# CMake Example Project

# Require
IF (WIN32)
    # Require 2.6 for Windows
    CMAKE_MINIMUM_REQUIRED (VERSION 2.6 FATAL_ERROR)
ELSE (WIN32)
    CMAKE_MINIMUM_REQUIRED (VERSION 2.4.7 FATAL_ERROR)
ENDIF (WIN32)

# Set your project name and version
PROJECT (CMakeExampleProject)
SET(PROJECT_VERSION 0.0.1 CACHE STRING "CMake Example Project Version Number")

# Build flags for different configurations
# Use is determined by the option CMAKE_BUILD_TYPE
set(CMAKE_C_FLAGS_DEBUG "-g -Wall ")
set(CMAKE_C_FLAGS_RELEASE "-O2 ")

set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall ")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 ")

# Add an uninstall target (for "make uninstall")
CONFIGURE_FILE(
  "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
  "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
  IMMEDIATE @ONLY)
ADD_CUSTOM_TARGET(uninstall
  "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")

# Add subdirectories so those CMakeLists.txt files are parsed  
ADD_SUBDIRECTORY (src)
ADD_SUBDIRECTORY (doc)

The command CMAKE_MINIMUM_VERSION defines the minimum version of CMake that is required to process the CMakeLists.txt file. This is to handle CMake API compatibility; as new functions are added, older versions of CMake won't be able process newer files. In general, we recommend version 2.6 and above.

The PROJECT macro sets your project's name. This can be useful in setting project install directories, but is not required. The PROJECT_VERSION variable is similar; if you're not creating a large project you probably don't need to worry about these variables.

The CMAKE_C_FLAGS and CMAKE_CXX_FLAGS are the build flags that will be applied to all C and C++ files in the project. You can set the CMake variable CMAKE_BUILD_TYPE to "DEBUG" or "RELEASE" to switch between the two types of build flags. This can be accomplished in the ccmake curses-based UI, or by using the -D option on the CMake command line. For example:

$ cmake -D CMAKE_BUILD_TYPE=RELEASE

CMake does not generate a "make uninstall" target by default. ADD_CUSTOM_TARGET allows you to add uninstall target. This particular method was borrowed from the CMake FAQ.

Finally, the ADD_SUBDIRECTORY commands tell CMake to traverse to the "doc" and "src" subdirectories and process the CMakeLists.txt files in those directories.

Note:
If you are installing Player on a machine, you need to make sure that the PKG_CONFIG_PATH that the code installed to is included in your search path. If you are using a bash shell, you can enter the following (or something similar) in your ~/.bashrc.
# for .pc in /usr/local
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig/

Source directory

A CMakeLists.txt contains directives on how to build, link, and install libraries and executables. We will discuss some of the macros that Player includes for building plugin drivers and client programs, but the rest are beyond the scope of the document. The following CMakeLists.txt file builds a Player C++ client program and a Player example driver library.

# If Player isn't installed to /usr, you'll have to edit this path to point
# to the location of UserPlayerC++.cmake and UsePlayerPlugin.cmake
SET (CMAKE_MODULE_PATH "/usr/local/share/cmake/Modules")

# These files include macros that make building against Player easier.  They provide
# the PLAYER_ADD* macros
INCLUDE (UsePlayerC++)
INCLUDE (UsePlayerPlugin)

# This command adds an example PlayerC++ client called 'exampleclient', using
# the 'exampleclient.cc' source file.
PLAYER_ADD_PLAYERCPP_CLIENT (exampleclient SOURCES exampleclient.cc)

# You can install the example client system-wide, using the INSTALL command.  The
# path is relative to CMAKE_INSTALL_PREFIX which defaults to /usr/local.  This will
# install the exampleclient to /usr/local/bin
INSTALL(exampleclient bin)

# If you also want to include a Player plugin driver, use these directions:
# This command adds an example plugin driver which will be called 'libexampledriver.so'
# which uses the source file 'exampleplugin.cc'
# PLAYER_ADD_PLUGIN_DRIVER (exampledriver SOURCES exampleplugin.cc)

Setting the CMAKE_MODULE_PATH tells CMake where it can search for additional modules when the INCLUDE command is used. If Player isn't installed to /usr, the path where Player is installed needs to be added so CMake will be able to find Player's CMake modules.

The INCLUDE command will load a CMake module for use in your CMakeLists.txt. The UsePlayerC++ and UsePlayerPlugin CMake modules provide useful macros for building and linking C++ clients and plugin drivers, respectively.

PLAYER_ADD_PLAYERCPP_CLIENT is one of the macros in UsePlayerC++. It uses the ADD_EXECUTABLE command from CMake to create an executable client program, and handles adding all of the include directories and link libraries needed to compile against Player. More details about the macros Player provides can be found on the Player wiki.

Generating documentation

To demonstrate another aspect of the CMakeLists.txt files we will setup code to allow you to run the command make doc and build doxygen comments. To use Doxygen to automatically generate documentation you typically type the command:

$ doxygen example.dox

where example.dox is a doxygen configuration file typically generated by a tool such as doxywizard. To accomplish the same thing with your Makefile, you can utilize the fact that you can create custom build targets with any arbitrary commands using @ CMake. The following creates a rudimentary build target to build documentation using Doxygen:

OPTION (BUILD_DOCUMENTATION "Build the doxygen-based documentation" OFF)
IF (BUILD_DOCUMENTATION)

    # Make sure Doxygen is on the system, if not then the documentation can't be built
    FIND_PACKAGE (Doxygen)
    IF (DOXYGEN_FOUND)

        # This is the doxyfile that will be used to generate the documentation
        # You can use programs like doxywizard to edit the settings
        SET (doxygenConfigFileIn "${CMAKE_CURRENT_SOURCE_DIR}/example.dox.in")
        SET (doxygenConfigFile "${CMAKE_CURRENT_BINARY_DIR}/example.dox")

        SET (DOXYGEN_LANGUAGE "English" CACHE STRING "Documentation language")
        MARK_AS_ADVANCED (DOXYGEN_LANGUAGE)

        # Using a .in file means we can use CMake to insert project settings
        # into the doxyfile.  For example, CMake will replace @PROJECT_NAME@ in
        # a configured file with the CMake PROJECT_NAME variable's value.
        CONFIGURE_FILE (${doxygenConfigFileIn} ${doxygenConfigFile} @ONLY)

        # Add the documentiation target.  This lets you run "make doc" from the
        # generated CMake makefiles
        ADD_CUSTOM_TARGET (doc
            ${DOXYGEN_EXECUTABLE} ${doxygenConfigFile}
            DEPENDS ${doxygenConfigFile}
            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
            VERBATIM)

        # You can add an "install" directive to install the resulting documentation
        # if desired.
    ELSE (DOXYGEN_FOUND)
        MESSAGE (STATUS "Documentation will not be built - Doxygen not found")
    ENDIF (DOXYGEN_FOUND)
ENDIF (BUILD_DOCUMENTATION)

Generating the build scripts

You need to run the CMake program in order to generate project @ Makefiles from the @ CMakeLists.txt files. Generally, it is encourged to do an "out of tree build." This constitutes creating a separate folder in the base directory of your project, usually called build. From this build folder, you can invoke CMake and generate the build scripts. The out-of-tree build has the advantage of keeping all of the generated build scripts away from the source files, so they can be easily removed or ignored using a .gitignore or .svnignore. You can do an out-of-tree build like this:

$ mkdir build
$ cd build
$ cmake ..

After generating the scripts, you can run the typical make command as usual to build the project.

Build options

After successfully running the CMake program the following options are avaliable for make

References