Index TableOfContents

Perform the following steps to get the [http://www.cmake.org CMake Make System] working for you. The example shows how to set things up such that the binary startCompletionServer is built by a call to make and that the distribution autocompletion-0.1.1-Source.tar.gz is built by a call to make package_source. It further demonstrates the basic work cycle and some advanced features of CMake.

Basic workcycle

Let us assume that all your source files (.h and .cpp files) are located in one top level directory called autocompletion. In this directory, create a file called CMakeLists.txt with the following content:

PROJECT(autocompletion)

SET(BASEFILES 
               Globals.cpp            Globals.h 
               HYBCompleter.cpp       HYBCompleter.h 
               IndexBase.cpp          IndexBase.h 
               History.cpp            History.h
               codes.cpp              codes.h
               nrutil.c               nrutil.h
               QueryParameters.cpp    QueryParameters.h 
               HYBIndex.cpp           HYBIndex.h 
               WordsFile.cpp          WordsFile.h 
               ConcurrentLog.cpp      ConcurrentLog.h 
               DocsDB.cpp             DocsDB.h 
               Document.cpp           Document.h 
               ExcerptsGenerator.cpp  ExcerptsGenerator.h
               CompletionServer.cpp   CompletionServer.h
               CompleterBase.cpp      CompleterBase.h
)

ADD_EXECUTABLE(startCompletionServer StartCompletionServer.cpp ${BASEFILES})

# TODO: the following ist not portable!
# we could use IF(UNIX) or IF($ENV{OSTYPE} EQUAL linux-gnu)

ADD_DEFINITIONS(-fexceptions
                -static
                 -O6 -Wall
                -DVERSION=\"'`date +\"Version %d%b%y %H:%M\"`'\"
                -DNDEBUG -D_FILE_OFFSET_BITS=64 
                -D_LARGEFILE_SOURCE -D_REENTRANT
                )

TARGET_LINK_LIBRARIES(startCompletionServer pthread z)

This tells CMake the name of the project, the basic .h and .cpp files that every executable is made from, the name and the dependencies of the executable startCompletionServer that you want to build, some compiler and linker options to be applied in every compiler and linker run, and the libraries against which startCompletionServer has to be linked.

Now create a sibling directory (or a directory anywhere out of the subtree rooted at autocompletion), change into this directory and make an "out-of-source" build:

$ cd ..
$ mkdir build
$ cd build
$ cmake ../autocompletion # out-of-source: build in a completely different tree
$ make
$ ./startCompletionServer

Some terminology

Before we go any further, let us clarify some important CMake terminology:

The source tree is where source files (*.c, etc.) and configuration files (CMakeList.txt, etc.) live.

The build tree is where you build the software. It contains everything that is configured or built. It has got a lot of files in it (e.g., *.o) that are essential to the build of executables or libraries, but which are not of interest to pure users of the software. It is possible to do an in-source build where the build tree is superimposed on the source tree, but it is just easier to keep the two separate since it allows you to entirely remove your build tree and start over without molesting your source tree. The latter is called an out-of-source build.

The install tree is where you install the subset of the build tree that is used by pure users. So it consists normally of just executables, libraries, and documentation. The various CMakeLists.txt files that are in the source tree specify exactly what is to be put in the install tree. The install tree should be kept entirely separate from the source tree and from the build tree.

A source package normally just contains a packager-specified subset of the source tree (e.g., the packager would want to skip .svn or CVS directories) in some packager-specified format (e.g., compressed tarball). A software packager can generate source packages of its software using CMake (via the CPack utility). Users of source packages obviously have to build the packaged software for themselves using CMake, but that gives them a lot of freedom since CMake configures the build for the special circumstances of their particular system. For example, it is very good at finding 3rd-party libraries required for the build that happen to be in non-standard locations or modifying the build (e.g., dropping parts of it) if certain 3rd-party components are not found.

A binary package contains everything that is in the install tree in some packager-specified format (e.g., compressed tarball). A software packager can generate binary packages of its software using CMake (via CPack). Users of binary packages do not have to build anything (they just use the executables and libraries that are supplied), but such binary packages demand a lot in terms of standardization. For example, they normally expect 3rd-party libraries to be of the same version and in the same location for the user as they were for the packager. So binary packages are great if built for a standard environment, such as a particular Linux distribution, but if there is any variation in the standard (e.g., Debian versus RedHat), the departure from the expected standards leads to trouble, and the user is better off building the software from a source package.

Installing

To specify that the executable startCompletionServer shall be installed into the bin subdirectory relative to the CMAKE_INSTALL_PREFIX, add the following line to CMakeLists.txt:

INSTALL(TARGETS startCompletionServer RUNTIME DESTINATION bin)

Now rerun CMake in the build tree, selecting the build tree top-level directory as the value for CMAKE_INSTALL_PREFIX:

$ cmake ../autocompletion -DCMAKE_INSTALL_PREFIX=.
$ make help

The last command shows you that you now have an install target that you can make:

$ make install
$ bin/startCompletionServer

As you can see, the executable has been installed into the bin subdirectory of your current (build tree) directory. Of course, at your customer's site the executable would not be installed into the build tree, but rather into the install tree located somewhere below /usr/local.

Packaging

To create a binary and a source package, add the following lines to CMakeLists.txt:

SET(CPACK_GENERATOR TGZ)
SET(CPACK_SOURCE_GENERATOR TGZ)
INCLUDE(CPack)

Now make the following targets:

$ make rebuild_cache
$ make help
$ make package
$ make package_source

make help shows you that you now have the additional targets package and package_source.

As you can see, your directory now contains the files autocompletion-0.1.1-Linux.tar.gz and autocompletion-0.1.1-Source.tar.gz

The CMake cache

You may wonder what the above target rebuild_cache is about.

When running CMake for the first time, you notice that it creates a file CMakeCache.txt. The cache is best thought of as a configuration file. Indeed Unix users could consider the cache as equivalent to the set of flags passed to the ./configure command. The first time CMake is run, it produces a CMakeCache.txt file. This file contains things such as the existence and location of a native JPEG library. The entries are added in response to certain CMake commands (e.g. FIND_LIBRARY) as they are processed anywhere in CMakeLists.txt files anywhere in the source tree. After CMake has been run and created a CMakeCache.txt file - you may edit it. The CMake GUI (ccmake .) will allow you to edit the options easily, or you can edit the file directly. The main reason for editing the cache would be to give CMake the location of a native library such as JPEG, or to stop it from using a native library and use a version of the library in your source tree.

CMake will not alter an existing entry in the cache file itself. If your CMakeLists.txt files change significantly, you will need to remove the relevant entries from the cache file. If you have not already hand-edited the cache file, you could just delete it before re-running CMake.

When running CMake from the command line, it is possible to specify command line options to CMake that will set values in the cache. This is done with a -DVARIABLE:TYPE=VALUE syntax on the command line.

Here is an example: Say that in your source code, a preprocessor macro

#if defined(USE_MEMSET)
...
#elif defined(USE_BZERO)
...
#endif

decides on whether you want to use the function memset() or bzero() to set the first n bytes of a byte area to zero. When you invoke CMake for the first time, you do it like this in your build tree;

$ cmake -DMEMSET:string=USE_MEMSET /path/to/source/tree

This defines a variable MEMSET in the CMake cache, with a default value of USE_MEMSET. You can alter the value of this variable in the cache by calling ccmake . or make edit_cache.

In your CMakeLists.txt, you have a section

IF(MEMSET STREQUAL USE_MEMSET)
ADD_DEFINITIONS(-DUSE_MEMSET)
ELSE(MEMSET STREQUAL USE_BZERO)
ADD_DEFINITIONS(-DUSE_BZERO)
ENDIF(MEMSET STREQUAL USE_MEMSET)

that passes on the correct macro value to the preprocessor.

Some projects are very complex and setting one value in the cache may cause new options to appear the next time the cache is built. The rule is to keep building the cache until it doesn't change. For most projects this will be just once. For some complicated ones it will be twice.

The rebuild_cache target runs CMake on the source tree and picks up additional cache entries if they exist.

Running CMake from the command line

From the command line, CMake can be run as an interactive question and answer session or as a non-interactive program. To run in interactive mode, just pass the option '-i' to cmake. This will cause CMake to ask you to enter a value for each value in the cache file for the project. The process stops when there are no longer any more questions to ask.

Using CMake to build a project in non-interactive mode is a simple process if the project does not have many options. For larger projects, using ccmake or cmake -i is recommended. This is because as you change options in the CMakeCache.txt file, CMake may add new entries to that file. It can be difficult to know when to stop the "run CMake and edit the cache file" cycle without the aid of an interface.

To build with just cmake, change directory into where you want the binaries to be placed. Run cmake and provide the path to the source code as its argument. Once you have edited the CMakeCache.txt file, rerun cmake, and repeat this process until you are happy with the cache settings. Then type make and your project should compile. Some projects will have install targets as well, so you can type make install to install them.

Testing

CMake comes with a testing framework, [http://www.cmake.org/Wiki/CMake#CTest CTest]. To use it, add the following lines to CMakeLists.txt:

ENABLE_TESTING()
ADD_EXECUTABLE(test-adler32 test-adler32.cpp ${BASEFILES})
TARGET_LINK_LIBRARIES(test-adler32 pthread z)
ADD_TEST(adler32 test-adler32 4711) # 4711 is an arbitrary input to test-adler32

Then type the following commands:

$ make rebuild_cache
$ make
$ make test

Creating libraries

You may have noticed that most of the object files for test-adler32 are built again, even though they already have been built for startCompletionServer. You may also have noticed that cmake is building files inside a build directory different for each target. It is really meant to be that way! (Normally you may have different compile flags for different targets so you want that.) If you do not want that then just put your BASEFILES in a "convenience library" and reuse it from both targets such as:

ADD_LIBRARY(base ${BASEFILES})

ADD_EXECUTABLE(target1 target1.cpp)
TARGET_LINK_LIBRARIES(target1 base)

ADD_EXECUTABLE(target2 target2.cpp)
TARGET_LINK_LIBRARIES(target2 base)

Using subdirectories

When your project becomes larger, you may want to divide it into several subdirectories, such as src, include, lib, doc, etc. For example, create a src subdirectory, move all your .h and .cpp files along with the current CMakeLists.txt into it, and create a new CMakeLists.txt in the top-level directory:

# top-level CMakeLists.txt

PROJECT(autocompletion)

# Recurse into subdirs
ADD_SUBDIRECTORY(src)           

CMake works recursively, descending from the current diretory into any directory listed in the ADD_SUBDIRECTORY command. Values of variables are inherited.

Generating and using config.h

If you want to write software that compiles and runs on different operating systems, you have to take care for the special properties of the different platforms. On different operating systems there are subtle differences, e.g. on FreeBSD you should not use malloc.h, while it is perfectly ok to use it on Linux. These differences are typically handled by providing a header file that contains a bunch of define-statements according to the platform properties. This file is usually named config.h and is listed as the first #include in every header file.

To create config.h, first create a template file config.h.cmake with, for example, the following content:

#cmakedefine HAVE_MALLOC_H 1
#cmakedefine HAVE_SYS_MOUNT_H 1
#cmakedefine HAVE_MEMCPY 1
#cmakedefine HAVE_FLOEDELDOE 1

#ifdef HAVE_MEMCPY
#define bla foo
#else
#define bla baz
#endif

Now add the following lines to your CMakeLists.txt:

INCLUDE (CheckIncludeFiles)

# usage: CHECK_INCLUDE_FILES (<header> <RESULT_VARIABLE> )
CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H)

# If another header is required to use the header you are looking for,
# you have to list the header files separated by semicolons
CHECK_INCLUDE_FILES ("sys/param.h;sys/mount.h" HAVE_SYS_MOUNT_H)

INCLUDE (CheckFunctionExists)
CHECK_FUNCTION_EXISTS(memcpy HAVE_MEMCPY)
CHECK_FUNCTION_EXISTS(floedeldoe HAVE_FLODELDOE)

CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake 
               ${CMAKE_CURRENT_BINARY_DIR}/config.h)

Now CMake will use the template file and the results of the various CHECK_ commands to create your config.h. For example, the following file could be created:

#define HAVE_MALLOC_H 1
#define HAVE_SYS_MOUNT_H 1
#define HAVE_MEMCPY 1
/* #undef HAVE_FLOEDELDOE 1 */

#ifdef HAVE_MEMCPY
#define bla foo
#else
#define bla baz
#endif

Useful commands

# Add include directories to the build.
INCLUDE_DIRECTORIES(/usr/include/nspr)

# Set the minimum required version of cmake for a project.
CMAKE_MINIMUM_REQUIRED(VERSION 2.4)

# the following line works in 2.4, but not in 2.0 :-)
# CMAKE_MINIMUM_REQUIRED(VERSION 2.4 FATAL_ERROR)

# workaround for stopping cmake
IF(CMAKE_SYSTEM_VERSION LESS 2.4)
  MESSAGE(FATAL_ERROR "STOPPED! You need at least CMake version 2.4 for this.")
ENDIF(CMAKE_SYSTEM_VERSION LESS 2.4)

# option to produce more 'verbose' compiling
SET(CMAKE_VERBOSE_MAKEFILE ON)

# set a variable to a value and test against this value
SET(BASERT USE_NSPR)
IF(BASERT STREQUAL USE_NSPR)
ADD_DEFINITIONS(-DUSE_NSPR)
ELSE(BASERT STREQUAL USE_NSPR)
ADD_DEFINITIONS(-DUSE_LINUX)
ENDIF(BASERT STREQUAL USE_NSPR)

You can finde more useful CMake variables on http://www.cmake.org/Wiki/CMake_Useful_Variables

Getting help and further reading

On your command line

$ man cmake
$ cmake --help
$ cmake --help-command CMAKE_MINIMUM_REQUIRED

On the web

[http://www.cmake.org/HTML/Documentation.html CMake Original Documentation]

[http://www.cmake.org/Wiki/CMake CMake Wiki]

[http://www.cmake.org/mailman/listinfo/cmake CMake Mailing List]

[http://www.cmake.org/Wiki/CMake_FAQ The CMake FAQ]

[http://www.cmake.org/cmake/help/syntax.html Quick syntax introduction]

CMake troubleshooting

Problem: You want to put all your object files into a static convenience library libconv.a. You want to link statically against this library and against all other (system) libraries, that is, you want to create a completely static executable, for example startCompletionServer, that you can send around to other people. But when you start the binary, you get

$ ./startCompletionServer
-bash: ./startCompletionServer: No such file or directory

Solution: Make sure that you use CMake 2.6. The above is a linker error produced by earlier versions of CMake, which mixes static with dynamic linking instructions.

CompleteSearch: completesearch/CMakeBuildSystem (last edited 2009-02-02 15:22:00 by mpiat1403)