Author: R. Koucha
Last update: 26-Apr-2020
Empirical approach to CMAKE
Introduction
Project 1: Generation of an executable
Project 2: Generation of an executable linked to a library
Project 3: Generation of an executable and a shared library with the same name
Project 4: Generation and installation of a program along with its manuals
Project 5: Installation of an executable and an API in a shared library along with the include files and manuals
Project 6: Packaging of a project
Project 7: Generation of a configuration file (config.h)
Project 8: Generation of a Linux user space program along with its kernel module
Project 9: Generation of a graphical executable based on Qt library
Open source projects using cmake
Resources
About the author

Introduction

cmake is a brand new building tool for software projects. It aims at replacing the old fashioned autotools.

cmake is open source and is well described here. Among the numerous provided features, we can list the following:


The purpose of this paper is to make the reader master some basic uses of cmake through some tiny software projects.


Project 1: Generation of an executable

The project called PROJECT1 is composed with the following sub-tree (the files can be downloaded from it):

   project1
     /\
    /  \
   /    \
 main.c simple

The project generates one target: an executable called simple. All the objects are stored into the top level directory.

The following sub-tree shows the files that have been added to build the project.

     project1
      /  |  \
     /   |   \
    /    |    \
   /     |     \
main.c simple CMakeLists.txt

Once the cmake files are setup, launch cmake to generate the build environment:

$ cd project1
$ cmake .
-- The C compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: .../project1

Then, the makefiles are ready to trigger a build with:

$ make
[100%] Building C object CMakeFiles/simple.dir/main.c.o
Linking C executable simple
[100%] Built target simple

After then, it is possible to clean-up anything generated by the environment with:

$ make clean
CMakeLists.txt in project1

The project1 directory is the top directory of the project. This is the place where we launch the cmake command.

This is the top CMakeLists.txt of the project.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
We identify the project as well as the programming language with the PROJECT directive.
We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.
We specify the target executable simple with the ADD_EXECUTABLE directive.

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(project1 C)

ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

ADD_EXECUTABLE(simple main.c)

Project 2: Generation of an executable linked to a library

The project called PROJECT2 is composed with the following sub-tree (the source files can be downloaded from it):

   project2
     /\
    /  \
   /    \
main.c  crypto

The project generates one target: an executable called crypto. All the objects are stored into the top level directory. The executable needs the standard library libcrypt.so.

The following sub-tree shows the files that have been added to build the project.

     project2
      /  |   \
     /   |    \
    /    |     \
   /     |      \
main.c  crypto CMakeLists.txt

Once the cmake files are setup, launch cmake to generate the build environment:

$ cd project2
$ cmake .
-- The C compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: .../project2

Then, the makefiles are ready to trigger a build with:

$ make
[100%] Building C object CMakeFiles/crypto.dir/main.c.o
Linking C executable crypto
[100%] Built target crypto

After then, it is possible to clean-up anything generated by the cmake environment with:

$ make clean
CMakeLists.txt in project2

The project2 directory is the top directory of the project. This is the place where we launch the cmake command.

This is the top CMakeLists.txt of the project.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
We identify the project as well as the programming language with the PROJECTdirective.
We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.
We specify the target executable crypto with the ADD_EXECUTABLE directive.
As crypto needs the libcrypt.so library, we specify it with the directive TARGET_LINK_LIBRARIES.

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(project2 C)

ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

ADD_EXECUTABLE(crypto main.c)

TARGET_LINK_LIBRARIES(crypto crypt)

Project 3: Generation of an executable and a shared library with the same name

The project called ROOF is composed with the following sub-tree:

                roof
            /  |  \     \
           /   |   \     \
          /    |    \     \
         /     |     \     \
        /      |      \     \
       /       |       \     \
      /        |        \     \
     /         |         \     \
   lib      client    include  bin
    |          |          |     |
    |          |          |     |
  roof.c     main.c    roof.h  main.o
  roof_p.h                     roof.o
                               roof
                               libroof.so

The project generates two targets: a shared library called libroof.so and an executable called roof.
All the objects are stored into the sub-directory bin.

The following sub-tree shows the files that have been added to build the project.

                            roof
            /       |       \     \        \
           /        |        \     \        \
          /         |         \     \        \
         /          |          \     \        \
        /           |           \     \        \
       /            |            \     \        \
      /             |             \     \        \
     /              |              \     \        \
   lib           client         include  bin   CMakeLists.txt
    |               |               |     |
    |               |               |     |
  roof.c          main.c         roof.h  main.o
  roof_p.h        CMakeLists.txt         roof.o
  CMakeLists.txt                         roof
                                         libroof.so

Once the cmake files are setup, launch cmake to generate the build environment:

$ cd roof
$ cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: .../roof

Then, the makefiles are ready to trigger a build with:

$ make
[ 50%] Building C object bin/CMakeFiles/roof.dir/roof.o
Linking C shared library libroof.so
[ 50%] Built target roof
[100%] Building C object bin/CMakeFiles/main.dir/main.o
Linking C executable roof
[100%] Built target main

After then, it is possible to clean-up anything generated by the cmake environment with:

$ make clean
CMakeLists.txt in roof

The directory roof is the top directory of the project. This is the place where we launch the cmake command.

This is the top CMakeLists.txt of the project.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
We identify the project as well as the programming language with the PROJECT directive.
We specify the directories where are located the include files (include and lib) with the INCLUDE_DIRECTORIES directive.
We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.
We specify the list of sub-directories where are located the sources to compile (lib and client) along with the target binary directory where are going the object files (bin) with the ADD_SUBDIRECTORY directive.

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(roof C)

INCLUDE_DIRECTORIES(include lib)
ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

ADD_SUBDIRECTORY(lib bin)
ADD_SUBDIRECTORY(client bin)
CMakeLists.txt in lib

The directory lib contains all the files to make the library libroof.so.

We assign a variable called roof_lib_src with the list of source files which will be used to build the shared library. There is actually only one file called roof.c. This is done with the SET directive.
Then we define the shared library to build through the directive ADD_LIBRARY. This directive is passed the name of the library (roof) to build libroof.so, the SHARED keyword to specify that it is a shared library and the list of source files which are part of the library with the variable roof_lib_src.

SET(roof_lib_src roof.c)

ADD_LIBRARY(roof SHARED ${roof_lib_src})
CMakeLists.txt in client

The directory client contains the files to make the executable roof.

We first define the target executable with ADD_EXECUTABLE directive.
We first assign a variable called roof_client_src with the list of source files which will be used to build the executable. There is actually only one file called main.c. This is done with the SET directive.
We define the dependency of the executable called roof with the libroof.so through the TARGET_LINK_LIBRARIES directive. There is a problem here as we would like to generate an executable called roof and a library called libroof.so. Normally, the directive should be written as:

ADD_EXECUTABLE(roof)
TARGET_LINK_LIBRARIES(roof roof)

But cmake complains saying:

CMake Error: Attempt to add link target roof of type: EXECUTABLE
to target roof. You can only link to STATIC or SHARED libraries.

In fact, cmake gets confused because it has the impression that we want to build roof with itself. Hence the use of an intermediate name main in the directives TARGET_LINK_LIBRARIES and ADD_EXECUTABLE. Then, we tell cmake to rename the generated main executable into roof thanks to the directive SET_TARGET_PROPERTIES.

SET(roof_client_src main.c)

ADD_EXECUTABLE(main ${roof_client_src})

TARGET_LINK_LIBRARIES(main roof)
SET_TARGET_PROPERTIES(main
                      PROPERTIES OUTPUT_NAME roof)

Project 4: Generation and installation of a program along with its manuals

The project called PROJECT4 builds an executable called pdip and a set of Linux manuals compressed with gzip tool.
The project is spreaded in the following sub-tree (the files can be downloaded from it):

project4
   |
   |
 pdip.c
 pdip_en.1
 pdip_fr.1

pdip.c is the source of the program and pdip_en.1 and pdip_fr.1 are respectively the english and french manuals.
The following sub-tree shows the files that have been added to build the project.

project4
   |
   |
 pdip.c
 pdip_en.1
 pdip_fr.1
 FindGZIP.cmake
 CMakeLists.txt
 pdip_chown.cmake

To generate the build environment:

$ cd project4
-- The C compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: .../project4

To trigger the build:

$ make
[ 33%] Building pdip_fr.1.gz
[ 66%] Building pdip_en.1.gz
Scanning dependencies of target pdip
[100%] Building C object CMakeFiles/pdip.dir/pdip.c.o
Linking C executable pdip
[100%] Built target pdip

To trigger the installation of the software:

$ make install

After then, it is possible to clean-up anything generated by the cmake environment with:

$ make clean
FindGZIP.cmake in project4

As the project needs to install the manuals in compressed format, we use the directive FIND_PROGRAM to check the presence of the gzip tool (specified after NAMES keyword). The directive is passed the list of directories to find into (/bin, /usr/bin and /usr/local/bin). The result of the command goes into the variable GZIP_TOOL passed as first parameter of the directive. If gzip is found, its path is stored into the variable GZIP_TOOL. If it is not found, GZIP_TOOL is assigned with GZIP_TOOL-NOTFOUND.

The result of the search is checked with the IF directive which returns true if the variable's value is not empty, 0,  N, NO, OFF, FALSE, NOTFOUND or <variable>-NOTFOUND. If the test is false, a message is displayed thanks to the MESSAGE directive with a weight set to FATAL_ERROR to stop any future processing.

FIND_PROGRAM(GZIP_TOOL
             NAMES gzip
             PATHS /bin
                   /usr/bin
                   /usr/local/bin)

IF(NOT GZIP_TOOL)
  MESSAGE(FATAL_ERROR "Unable to find 'gzip' program")
ENDIF(NOT GZIP_TOOL)
CMakeLists.txt in project4

This project contains only one CMakeLists.txt file.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
The project's name project4 as well as the used programming language (C) are declared with the directive PROJECT.
Then we include the file FindGZIP.cmake  to trigger the search of the gzip tool (INCLUDE directive).
If gzip tool has been found, its pathname is stored in the variable GZIP_TOOL.
Three variables are set with the SET directive:

In the loop (FOREACH directive), we define the targets of the manuals and the way to build them through the directive ADD_CUSTOM_COMMAND. This directive also lists the dependencies (DEPENDS) of the target to trigger the build only if the source manuals have been modified. The COMMENT tag is useful to make cmake display the string "Building pdip_....1.gz" while triggering the command.

We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.

The entry point of the build is the ADD_EXECUTABLE directive which specifies that the output program pdip depends on the source code pdip.c and the compressed manuals pdip_en.1.gz and pdip_fr.1.gz.

The following directives describe the installation of the software. The installation is done from the directory specified by the content of the variable CMAKE_INSTALL_PREFIX. It defaults to /usr/local:
cmake does not provide any facilities to change the owner and group identifiers. We must specify a script to launch at the end of the installation with the directive INSTALL(SCRIPT...). The script called pdip_chown.cmake is written in cmake language and merely changes the owner and group to root by calling the shell chown and chgrp programs through the directive EXECUTE_PROCESS.

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(project4 C)

# Search for gzip program
INCLUDE (FindGZIP.cmake)

SET(pdip_src pdip.c)
SET(pdip_exe pdip)
SET(pdip_man_src pdip_en.1 pdip_fr.1)
SET(pdip_man_gz pdip_en.1.gz pdip_fr.1.gz)

# Compression of the manuals
FOREACH(man ${pdip_man_src})
  ADD_CUSTOM_COMMAND(OUTPUT ${man}.gz
                     COMMAND ${GZIP_TOOL} -c ${man} > ${man}.gz
                     DEPENDS ${man}
                     COMMENT "Building ${man}.gz")
ENDFOREACH(man)

# Compilation options passed to the compiler
ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

# Build of the program
ADD_EXECUTABLE(${pdip_exe} ${pdip_src} ${pdip_man_gz})

# Installation of the program
INSTALL(TARGETS pdip
        DESTINATION "bin"
        PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)

# Installation of the manuals
INSTALL(FILES pdip_fr.1.gz
        DESTINATION "share/man/fr/man1"
        PERMISSIONS OWNER_READ GROUP_READ WORLD_READ RENAME pdip.1.gz)
INSTALL(FILES pdip_en.1.gz
        DESTINATION "share/man/man1"
        PERMISSIONS OWNER_READ GROUP_READ WORLD_READ RENAME pdip.1.gz)

# Script to be executed at installation time (kind of post-intallation script) to
# change the right accesses on the installed files
INSTALL(SCRIPT pdip_chown.cmake)
pdip_chown.cmake in project4
# Copy the files to the destination directory
EXECUTE_PROCESS(COMMAND chown root ${CMAKE_INSTALL_PREFIX}/bin/pdip
                COMMAND chgrp root ${CMAKE_INSTALL_PREFIX}/bin/pdip
                COMMAND chown root ${CMAKE_INSTALL_PREFIX}/share/man/fr/man1/pdip.1.gz
                COMMAND chgrp root ${CMAKE_INSTALL_PREFIX}/share/man/fr/man1/pdip.1.gz
                COMMAND chown root ${CMAKE_INSTALL_PREFIX}/share/man/man1/pdip.1.gz
                COMMAND chgrp root ${CMAKE_INSTALL_PREFIX}/share/man/man1/pdip.1.gz)

Project 5: Installation of an executable and an API in a shared library along with the include files and manuals

The project called ROOF is composed with the following sub-tree:

               roof
           /   |  \        \
          /    |   \        \
         /     |    \        \
        /      |     \        \
       /       |      \        \
      /        |       \        \
     /         |        \        \
    /          |         \        \
  lib       client     include    man
   |           |          |        |
   |           |          |        |
 roof.c      main.c    roof.h    roof.1
 roof_p.h                        roof.3
                                 ...
                                 roof.7

The project generates the following targets:

The following sub-tree shows the files that have been added to build the project.

                        roof
           /      |        \              \            \
          /       |         \              \            \
         /        |          \              \            \
        /         |           \              \            \
       /          |            \              \            \
      /           |             \              \            \
     /            |              \              \            \
    /             |               \              \            \
  lib          client           include          man     CMakeLists.txt
   |              |                |              |
   |              |                |              |
 roof.c         main.c          roof.h          roof.1
 roof_p.h       CMakeLists.txt  CMakeLists.txt  roof.3
 CMakeLists.txt                                 ...
                                                roof.7
                                                CMakeLists.txt
                                                FindGZIP.cmake

Once the cmake files are setup, launch cmake to generate the build environment (with /tmp as target installation directory instead of the default /usr/local):

$ cd roof
$ cmake . -DCMAKE_INSTALL_PREFIX=/tmp
-- Configuring done
-- Generating done
-- Build files have been written to: .../roof

Then, the makefiles are ready to trigger a build with:

$ make
[ 50%] Building C object bin/CMakeFiles/roof.dir/roof.o
Linking C shared library libroof.so
[ 50%] Built target roof
[100%] Building C object bin/CMakeFiles/main.dir/main.o
Linking C executable roof
[100%] Built target main

To install the files in /tmp, just invoke:

$ make install
[  3%] Built target roof
[  7%] Built target main
[100%] Built target man
Install the project...
-- Install configuration: ""
-- Install configuration: ""
-- Installing /tmp/lib/libroof.so.1.0.0
-- Install configuration: ""
-- Installing /tmp/bin/roof
-- Install configuration: ""
-- Installing /tmp/share/man/man1/roof.1.gz
-- Installing /tmp/share/man/man3/roof_cwd.3.gz
-- Installing /tmp/share/man/man3/roof_login.3.gz
...

After then, it is possible to clean-up anything generated by the cmake environment with:

$ make clean
CMakeLists.txt in roof

The directory roof is the top directory of the project. This is the place where we launch the cmake command.

This is the top CMakeLists.txt of the project.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
We identify the project as well as the programming language with the PROJECT directive.
We specify the directories where are located the include files (include and lib) with the INCLUDE_DIRECTORIES directive.
We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.
We specify the list of sub-directories where are located the sources to build (lib, client, man and include) with the ADD_SUBDIRECTORY directive. We don't specify a directory (second argument of ADD_SUBDIRECTORY) where to put all the binaries because we faced some problems for the installation of the shared library (Bug in cmake ?).


CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(roof C)

INCLUDE_DIRECTORIES(include lib)

ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

ADD_SUBDIRECTORY(lib)
ADD_SUBDIRECTORY(client)
ADD_SUBDIRECTORY(man)
ADD_SUBDIRECTORY(include)
CMakeLists.txt in lib

The directory lib contains all the files to make the library libroof.so.

We define the shared library to build through the directive ADD_LIBRARY. This directive is passed the name of the library (roof) to build libroof.so, the SHARED keyword to specify that it is a shared library and the list of source files which are part of the library: roof.c.
We define the build version (VERSION) and the API version (SOVERSION) of the library through the SET_TARGET_PROPERTIES directive.
The installation of the library is specified with the INSTALL directive. It is said that the destination directory from the installation tree will be lib. The installation tree is specified by the variable CMAKE_INSTALL_PREFIX. By default, it is /usr/local.

# Make shared library libroof.so from 'roof.c'
ADD_LIBRARY(roof SHARED roof.c)

# Set the build version (VERSION) and the API version (SOVERSION)
SET_TARGET_PROPERTIES(roof PROPERTIES VERSION 1.0.0 SOVERSION 1) # Installation of the library INSTALL(TARGETS roof DESTINATION lib PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
CMakeLists.txt in client

The directory client contains the files to make the executable roof.

We first define the target executable roof with ADD_EXECUTABLE directive.
We define the dependency of the executable with the libroof.so through the TARGET_LINK_LIBRARIES directive. There is a problem here as we would like to generate an executable called roof and a library called libroof.so. Normally, the directive should be written as:

ADD_EXECUTABLE(roof)
TARGET_LINK_LIBRARIES(roof roof)

But cmake complains saying:

CMake Error: Attempt to add link target roof of type: EXECUTABLE
to target roof. You can only link to STATIC or SHARED libraries.

In fact, cmake gets confused because it has the impression that we want to build roof with itself. Hence the use of an intermediate name main in the directives TARGET_LINK_LIBRARIES and ADD_EXECUTABLE. Then, we tell cmake to rename the generated main executable into roof thanks to the directive SET_TARGET_PROPERTIES.

The installation of the program is specified with the INSTALL directive. It is said that the destination directory from the installation tree will be bin. The installation tree is specified by the variable CMAKE_INSTALL_PREFIX. By default, it is /usr/local.

# 'main' depends on some C source files
ADD_EXECUTABLE(main main.c)

# 'main' depends on libroof.so
TARGET_LINK_LIBRARIES(main roof)

# In the preceding rules, we can't use 'roof' as target name otherwise
# cmake will return in error with TARGET_LINK_LIBRARIES(roof roof):
#
#    CMake Error: Attempt to add link target roof of type: EXECUTABLE
#    to target roof. You can only link to STATIC or SHARED libraries.
#
# Hence the SET_TARGET_PROPERTIES to rename main to roof
#
SET_TARGET_PROPERTIES(main
                      PROPERTIES OUTPUT_NAME roof)

# Installation of the program
INSTALL(TARGETS main
        RUNTIME
        DESTINATION bin
        PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
CMakeLists.txt in include

The installation of the include files is specified with the INSTALL directive. It is said that the destination directory from the installation tree will be include. The installation tree is specified by the variable CMAKE_INSTALL_PREFIX. By default, it is /usr/local.

INSTALL(FILES roof.h
        DESTINATION include
        PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
FindGZIP.cmake in man

As the project needs to install the manuals in compressed format, we use the directive FIND_PROGRAM to check the presence of the gzip tool (specified after NAMES keyword). The directive is passed the list of directories to find into (/bin, /usr/bin and /usr/local/bin). The result of the command goes into the variable GZIP_TOOL passed as first parameter of the directive. If gzip is found, its path is stored into the variable GZIP_TOOL. If it is not found, GZIP_TOOL is assigned with GZIP_TOOL-NOTFOUND.

The result of the search is checked with the IF directive which returns TRUE if the variable's value is not empty, 0,  N, NO, OFF, FALSE, NOTFOUND or <variable>-NOTFOUND. If the test is FALSE, a message is displayed thanks to the MESSAGE directive with a weight set to FATAL_ERROR to stop any future processing.

FIND_PROGRAM(GZIP_TOOL
             NAMES gzip
             PATHS /bin
                   /usr/bin
                   /usr/local/bin
)

IF(NOT GZIP_TOOL)
  MESSAGE(FATAL_ERROR "Unable to find 'gzip' program")
ENDIF(NOT GZIP_TOOL)
CMakeLists.txt in man

We first include the script FindGZIP.cmake to check the presence of gzip tool.
We use SET directive to set three variables with the list of source manuals.
Thanks to the string manipulation directive STRING on the preceding variables, we define three other variables containing the list of compressed manuals (suffix .gz).
For each source manual, we use ADD_CUSTOM_COMMAND to generate the specific command which will compress the source manuals.
We define the target man to be added to the default build target so that it will be run every time through the directive ADD_CUSTOM_TARGET. But we make it depend on the source manuals to avoid rebuilding the manuals each time we invoke make.
The installation of the compressed manuals is specified with the INSTALL directives. It is said that the destination directory from the installation tree will be share/man/manX. The installation tree is specified by the variable CMAKE_INSTALL_PREFIX. By default, it is /usr/local.

# Search for gzip program
INCLUDE (FindGZIP.cmake)

# Lists of source manuals
SET(roof_man_src_1 roof.1)
SET(roof_man_src_3 roof_cwd.3
                   roof_login.3
                   roof_pwd.3
                   roof_syst.3
                   roof_delete.3
                   roof_mkdir.3
                   roof_retr.3
                   roof_type.3
                   roof.3
                   roof_get_debug_level.3
                   roof_mv.3
                   roof_rm.3
                   roof_get_reply.3
                   roof_new.3
                   roof_rmdir.3
                   roof_cdup.3
                   roof_initialize.3
                   roof_nlst.3
                   roof_set_debug_level.3
                   roof_close_ctrl.3
                   roof_list.3
                   roof_open_ctrl.3
                   roof_stor.3)
SET(roof_man_src_7 roof.7)

# Lists of compressed manuals
STRING(REGEX REPLACE ".1" ".1.gz" roof_man_gz_1 "${roof_man_src_1}")
STRING(REGEX REPLACE ".3" ".3.gz" roof_man_gz_3 "${roof_man_src_3}")
STRING(REGEX REPLACE ".7" ".7.gz" roof_man_gz_7 "${roof_man_src_7}")


# Compression of the manuals
FOREACH(man ${roof_man_src_1} ${roof_man_src_3} ${roof_man_src_7})
  ADD_CUSTOM_COMMAND(OUTPUT ${man}.gz
                     COMMAND ${GZIP_TOOL} -c ${man} > ${man}.gz
                     DEPENDS ${man}
                     COMMENT "Building ${man}.gz")
ENDFOREACH(man)

# Add the manual generation in the global rules
ADD_CUSTOM_TARGET(man ALL
                  DEPENDS ${roof_man_gz_1} ${roof_man_gz_3} ${roof_man_gz_7})


# Installation of the manuals
INSTALL(FILES ${roof_man_gz_1}
        DESTINATION "share/man/man1"
        PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
INSTALL(FILES ${roof_man_gz_3}
        DESTINATION "share/man/man3"
        PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
INSTALL(FILES ${roof_man_gz_7}
        DESTINATION "share/man/man7"
        PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)

Project 6: Packaging of a project

On this resource, it is explained how to package a project. Loosely speaking, cmake is associated to the CPack tool to manage the packages. The following formats are available:

To get an application of CPACK in a real project (with RPM and DEB packaging), refer to the PDIP software maintained by the author of this article.

In this project, we generate a package for project 5. We just need to add the directive INCLUDE(CPack) in the top CMakeLists.txt file. Optionally, it is possible to set some configuration variables for CPack like CPACK_PACKAGE_VERSION_MAJOR, CPACK_PACKAGE_VERSION_MINOR, CPACK_PACKAGE_VERSION_PATCH...
This generates a new target called package in the build system. When this target is built, CPack is invoked to generate all of the packages. Internally, CPack uses the CMake's install directives to make the package.

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(roof C)

INCLUDE_DIRECTORIES(include lib)

ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

ADD_SUBDIRECTORY(lib)
ADD_SUBDIRECTORY(client)
ADD_SUBDIRECTORY(man)
ADD_SUBDIRECTORY(include)

SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Remote Operations On Files")
SET(CPACK_PACKAGE_VENDOR "Rachid Koucha")
#SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt")
SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING")
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "0")
SET(CPACK_PACKAGE_VERSION_PATCH "0")
SET(CPACK_PACKAGE_INSTALL_DIRECTORY "CMake ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}")
#SET(CPACK_STRIP_FILES "bin/MyExecutable")
#SET(CPACK_SOURCE_STRIP_FILES "")
SET(CPACK_PACKAGE_EXECUTABLES "roof" "FTP client")
INCLUDE(CPack)

Then, invoke the following to trigger the package generation:

$ make package
[  3%] Built target roof
[  7%] Built target main
[100%] Built target man
Linking C executable CMakeFiles/CMakeRelink.dir/roof
Run CPack packaging tool...
CPack: Create package using STGZ
CPack: Install projects
CPack: - Run preinstall target for: roof
CPack: - Install project: roof
CPack: Compress package
CPack: Finalize package
CPack: Package .../roof/roof-1.0.0-Linux.sh generated.
CPack: Create package using TGZ
CPack: Install projects
CPack: - Run preinstall target for: roof
CPack: - Install project: roof
CPack: Compress package
CPack: Finalize package
CPack: Package .../roof/roof-1.0.0-Linux.tar.gz generated.
CPack: Create package using TZ
CPack: Install projects
CPack: - Run preinstall target for: roof
CPack: - Install project: roof
CPack: Compress package
CPack: Finalize package
CPack: Package .../roof/roof-1.0.0-Linux.tar.Z generated.

As we can see, this generates the STGZ, the TGZ and the TZ packages. The packages do not contain the sources but only the binaries.

Installation from the STGZ into /tmp directory:

$ ./roof-1.0.0-Linux.sh --help
Usage: ./roof-1.0.0-Linux.sh [options]
Options: [defaults in brackets after descriptions]
  --help            print this message
  --prefix=dir      directory in which to install
  --include-subdir  include the roof-1.0.0-Linux subdirectory
  --exclude-subdir  exclude the roof-1.0.0-Linux subdirectory

$ ./roof-1.0.0-Linux.sh --prefix=/tmp --exclude-subdir
roof Installer Version: 1.0.0, Copyright (c) Rachid Koucha
This is a self-extracting archive.
The archive will be extracted to: /tmp

Using target directory: /tmp
Extracting, please wait...

Unpacking finished successfully
$

Project 7: Generation of a configuration file (config.h)

The project called PROJECT7 is composed with the following sub-tree (the files can be downloaded from it):

    project7
       /\
      /  \
     /    \
main.c   simple

The project generates one target: an executable called simple. All the objects are stored into the top level directory. The file main.c needs some configuration macros that are located in a header file called config.h. The latter will be built automatically by cmake as it is setup thanks to some cmake variables.

The following sub-tree shows the files that have been added to build the project.

                 project7
       /        /        |       \
      /        /         |        \
     /        /          |         \
    /        /           |          \
main.c  config.h.cmake simple  CMakeLists.txt

Once the cmake files are setup, launch cmake to generate the build environment:

$ cd project7
$ cmake .
-- The C compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: .../project7

Then, the makefiles are ready to trigger a build with:

$ make
Scanning dependencies of target simple
[100%] Building C object CMakeFiles/simple.dir/main.c.o
Linking C executable simple
[100%] Built target simple

After then, it is possible to clean-up anything generated by the cmake environment with:

$ make clean
CMakeLists.txt in project7

The directory project7 is the top directory of the project. This is the place where we launch the cmake command.

This is the top CMakeLists.txt of the project.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
We identify the project as well as the programming language with the PROJECT directive.
Thanks to the SET directive, we define a set of variables (PROJECT_NAME, SIMPLE_VERSION) that will be used to generate the file config.h.
We make the header file config.h via the directive CONFIGURE_FILE. It is passed a file skeleton (config.h.cmake) which will be converted into config.h with some substitutions.
We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.
We specify the target executable simple with the ADD_EXECUTABLE directive.

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(project7 C)

SET(PROJECT_NAME SIMPLE)

SET(SIMPLE_MAJOR 1)
SET(SIMPLE_MINOR 8)
SET(SIMPLE_PATCH 6)
SET(SIMPLE_VERSION ${SIMPLE_MAJOR}.${SIMPLE_MINOR}.${SIMPLE_PATCH})


CONFIGURE_FILE(config.h.cmake config.h)

ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

ADD_EXECUTABLE(simple main.c)
config.h.cmake in project7

This file is the skeleton of config.h: all the strings embraced by the character @ (e.g. @PROJECT_NAME@) are placeholders. They are substituted by the cmake variables with the same name (e.g. ${PROJECT_NAME}).

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// File        : config.h
// Description : Configuration of @PROJECT_NAME@
//
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#ifndef CONFIG_H
#define CONFIG_H

//---------------------------------------------------------------------------
// Name : SIMPLE_VERSION
// Usage: Version of @PROJECT_NAME@
//----------------------------------------------------------------------------
#define SIMPLE_VERSION "@SIMPLE_VERSION@"

#endif // CONFIG_H
config.h in project7

The file config.h is the result of the substitutions done by the directive CONFIGURE_FILE into the file config.h.cmake.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// File        : config.h
// Description : Configuration of SIMPLE
//
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#ifndef CONFIG_H
#define CONFIG_H

//---------------------------------------------------------------------------
// Name : SIMPLE_VERSION
// Usage: Version of SIMPLE
//----------------------------------------------------------------------------
#define SIMPLE_VERSION "1.8.6"

#endif // CONFIG_H

Project 8: Generation of a Linux user space program along with its kernel module

The project called PROJECT8 is composed with the following sub-tree (the files can be downloaded from it):

                           project8
             /             /     \     \      \
            /             /       \     \      \
           /             /         \     \      \
          /             /           \     \      \
udp_console_u.c  udpc_console_k.c  udpc Kbuild udpc.ko 

The project generates two targets: a user space executable called udpc and a kernel module called udpc.ko. All the objects are stored into the top level directory. The file udpc_console_u.c needs some configuration macros that are located in a header file called config.h. The latter is built automatically by cmake as it is setup thanks to some cmake variables.

The following sub-tree shows the files that have been added to build the project.

                                project8
             /             /           |        \      \        \         \
            /             /            |         \      \        \         \
           /             /             |          \      \        \         \
          /             /              |           \      \        \         \
udp_console_u.c  udpc_console_k.c  config.h.cmake  udpc  Kbuild  udpc.ko  CMakeLists.txt

Once the cmake files are setup, launch cmake to generate the build environment:

$ cd project8
$ cmake .
-- Building UDPC version 1.0.0
-- Configuring done
-- Generating done
-- Build files have been written to: .../project8

Then, the makefiles are ready to trigger a build with:

$ make
Scanning dependencies of target kudpc
[  0%] Building udpc.ko
[ 50%] Built target kudpc
Scanning dependencies of target udpc
[100%] Building C object CMakeFiles/udpc.dir/udp_console_u.o
Linking C executable udpc
[100%] Built target udpc

After then, it is possible to clean-up anything generated by the cmake environment with:

$ make clean
CMakeLists.txt in project8

The directory project8 is the top directory of the project. This is the place where we launch the cmake command.

This is the top CMakeLists.txt of the project.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
We identify the project as well as the programming language with the PROJECT directive.
Thanks to the SET directive, we define a set of variables (PROJECT_NAME, UDPC_VERSION) that will be used to generate the file config.h.
We make the header file config.h via the directive CONFIGURE_FILE. It is passed a file skeleton (config.h.cmake) which will be converted into config.h with some substitutions.
We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.
We specify the target executable udpc with the ADD_EXECUTABLE directive.
To build the Linux kernel module:

Then the kernel module and the executable are installed through the INSTALL directive.

cmake_minimum_required(VERSION 2.4)

PROJECT(udpc C)

SET(PROJECT_NAME UDPC)

# Version number
SET(UDPC_MAJOR 1)
SET(UDPC_MINOR 0)
SET(UDPC_PATCH 0)
SET(UDPC_VERSION ${UDPC_MAJOR}.${UDPC_MINOR}.${UDPC_PATCH})


MESSAGE(STATUS "Building UDPC version ${UDPC_VERSION}")

CONFIGURE_FILE(config.h.cmake config.h)


SET(udpc_src udp_console_u.c)
SET(udpc_exe udpc)

ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Winline -Werror)

# Build of the program
ADD_EXECUTABLE(${udpc_exe} ${udpc_src})

# Build the module
EXECUTE_PROCESS(COMMAND uname -r
                OUTPUT_VARIABLE os_release
                OUTPUT_STRIP_TRAILING_WHITESPACE)
SET(module_path /lib/modules/${os_release})
SET(module_build_path ${module_path}/build)

ADD_CUSTOM_COMMAND(OUTPUT udpc.ko
                   COMMAND make -C ${module_build_path} M=`pwd`
                   DEPENDS udp_console_k.c Kbuild
                   COMMENT "Building udpc.ko"
                  )

ADD_CUSTOM_TARGET(kudpc ALL DEPENDS udpc.ko)


# Installation of the module
SET(module_install_path ${module_path}/kernel)
INSTALL(FILES udpc.ko
        DESTINATION ${module_install_path}
        PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)

# Installation of the program
INSTALL(TARGETS udpc
        DESTINATION "bin"
        PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
config.h.cmake in project8

This file is the skeleton of config.h: all the strings embraced by the character @ (e.g. @PROJECT_NAME@) are substituted by the cmake variables with the same name (e.g. ${PROJECT_NAME}).

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// File        : config.h
// Description : Configuration of @PROJECT_NAME@
// License     :
//
//  Copyright (C) 2011 Rachid Koucha <rachid dot koucha at free dot fr>
//
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//
// Evolutions  :
//
//     25-Jan-2011  R. Koucha           - Creation
//
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#ifndef CONFIG_H
#define CONFIG_H

//---------------------------------------------------------------------------
// Name : @PROJECT_NAME@_VERSION
// Usage: Version of @PROJECT_NAME@
//----------------------------------------------------------------------------
#define @PROJECT_NAME@_VERSION "@UDPC_VERSION@"

#endif // CONFIG_H
config.h in project8

The file config.h is the result of the substitutions done by the directive CONFIGURE_FILE into the file config.h.cmake.

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// File        : config.h
// Description : Configuration of UDPC
// License     :
//
//  Copyright (C) 2011 Rachid Koucha <rachid dot koucha at free dot fr>
//
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//
// Evolutions  :
//
//     25-Jan-2011  R. Koucha           - Creation
//
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#ifndef CONFIG_H
#define CONFIG_H

//---------------------------------------------------------------------------
// Name : UDPC_VERSION
// Usage: Version of UDPC
//----------------------------------------------------------------------------
#define UDPC_VERSION "1.0.0"

#endif // CONFIG_H
Kbuild in project8

The file Kbuild as explained in Linux documentation embedded in the sources of the kernel (.../Documentation/kbuild/modules.txt and .../Documentation/kbuild/kbuild.txt) to build a kernel module:

# Driver for the UDP based Linux console
obj-m += udpc.o
udpc-objs := udp_console_k.o

Project 9: Generation of a graphical executable based on Qt library

The project called PROJECT9 is composed with the following sub-tree (the files can be downloaded from it):

            project9
       /         |        \
      /          |         \
     /           |          \
    /            |           \
hello.cc   CMakeLists.txt  hello

The project generates one target: an executable called hello. All the objects are stored into the top level directory.

Once the cmake files are setup, launch cmake to generate the build environment:

$ cd project9
$ cmake .
-- The CXX compiler identification is GNU
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
Found Qt include files in /usr/include/qt4
Found Qt libraries in /usr/lib
-- Configuring done
-- Generating done
-- Build files have been written to: .../project9

Then, the makefiles are ready to trigger a build with:

$ make
Scanning dependencies of target hello
[100%] Building CXX object CMakeFiles/hello.dir/hello.o
Linking CXX executable hello
[100%] Built target hello

The executable is a simple "Hello world" program that uses Qt graphical API. The call to the executable triggers the following:

$ ./hello &

      hello.png

After then, it is possible to clean-up anything generated by the cmake environment with:

$ make clean
CMakeLists.txt in project9

The directory project9 is the top directory of the project. This is the place where we launch the cmake command.

This is the top CMakeLists.txt of the project.
We define the minimum version of cmake that we support for this project through the directive CMAKE_MINIMUM_REQUIRED.
We identify the project as well as the programming language with the PROJECT directive. CXX stands for C++ language.
As the program needs Qt include files, we look for the location of those files thanks to the FIND_PATH directive. The trick consists to look for a well known sub-directory name (i.e. QtGui) belonging to the Qt environment. FIND_PATH stores the result of the search into the QT_INC_DIR variable. The content of this variable is checked and we extract the root directory of the include files thanks to the STRING directive.
We define the flags that will be passed as arguments to the compiler through the ADD_DEFINITIONS directive. It is optional to do it but the default flags passed to the compiler are not sufficiently severe to check and report the warnings and bugs.
We specify the include directory thanks to the INCLUDE_DIRECTORIES directive.
We specify the target executable hello with the ADD_EXECUTABLE directive and the libraries to link with it thanks the TARGET_LINK_LIBRARIES directive.

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

PROJECT(hello CXX)

FIND_PATH(QT_INC_DIR
          NAMES QtGui
          PATHS /usr/include
                /usr/include/qt4)

IF(NOT QT_INC_DIR)

  MESSAGE(FATAL_ERROR "Unable to find Qt include files")
ELSE(NOT QT_INC_DIR) STRING(REGEX REPLACE "/QtGui" "" QT_INC_DIR "${QT_INC_DIR}") MESSAGE("Found Qt include files in " ${QT_INC_DIR}) ENDIF(NOT QT_INC_DIR) ADD_DEFINITIONS(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wshadow -Wpointer-arith -Wcast-qual -Winline -Werror) INCLUDE_DIRECTORIES(${QT_INC_DIR}) ADD_EXECUTABLE(hello hello.cc) TARGET_LINK_LIBRARIES(hello QtCore QtGui)

Open source projects using cmake

Here are links on open source software using cmake:

Resources
About the author
The author is an engineer in computer sciences located in France. He can be contacted here.