A tutorial for CMake – Chapter 2: libraries, installation, message

Review of the last chapter

In the last chapter, we talked about how to use CMake and a BASH script to conveniently call cmake in the build/ folder, and some basic commands of CMake. We talked about how to add an executable as the build target, and how to set where to put the binary files and library files.

In this chapter, we will talk about creating libraries with CMake, how to do installation into user-defined directory, and how to output messages from CMake.

Comments in CMake

A # comments until the end of the line;

A #[[ comments until the next closing brackets ]].

Creating libraries with CMake

In anther post of mine, I talked about the differences of the C/C++ static and shared(dynamic) libraries. In this section, we’ll see how to set CMake to make them.

To add a library to the project using the specified source files, use

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            source1 [source2 ...])

where <name> is the library target name, this library name needs to be unique in the CMake project. It’s not the library file name. [STATIC | SHARED | MODULE] (choose one of the 3 options) specifies the library type (see my intro. on the difference, for example).

The default value of the [STATIC | SHARED | MODULE] option depends on a global CMake variable BUILD_SHARED_LIBS. If it’s set to ON, SHARED will be used; otherwise, STATIC is the default.

The library will be created in the ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} (for STATIC libraries) or the ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}.

CMake Installation

You may be familiar with make install, which will install the executables and libraries to your system paths (such that you need the admin. privilege to authorize). To be able to enable this with the CMake-generated Makefile, you need to tell CMake to make this rule. A detailed description of all the forms of installation is available at here, but we only show two installation forms in this section as examples.

First one is the installation of TARGETS. In this mode you specify the target name you defined in the preceding CMake file that you would like to install to the destination. A common use is

install(TARGETS target_name DESTINATION dir_name)

Remember that the dir_name for DESTINATION will be preceded by a CMake system variable: ${CMAKE_INSTALL_PREFIX}, such that the full installation destination is actually ${CMAKE_INSTALL_PREFIX}/dir_name. This variable is defaulted to be /usr/local on UNIX and C:/Program Files/ on WINDOWS.

This target_name can be a target you defined for a library file, using add_library, or executable (runtime) you defined using add_executable. The install command will put the target file to the specified DESTINATION.

CMake Messages

One of the most important things one needs to learn is to print messages on the screen for the user. In CMake, this is done by

message([<mode>] "message to display" ...)

The full description of this command can be found here. In general the message follows the BASH convention, such that the variable expansion is done by ${var}. For instance, you can print out the system detected C/C++ compilers by

message(STATUS "C compiler is ${CMAKE_C_COMPILER}")
message(STATUS "C++ compiler is ${CMAKE_CXX_COMPILER}")

Examples for this chapter

We will modify and expand our example in the last chapter to reflect the contents in this chapter. First of all, instead of directly build an executable with the source .cpp files, we will compile the Complex.cpp to a shared library first, then link the main program HelloComplex.cpp to the library.

The tree of the directory looks the same as before:

The contents of the CMakeLists.txt are pasted here:

cmake_minimum_required(VERSION 2.6)
project(HelloComplex)

# First of all set up some basic stuff
enable_testing()
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
if (APPLE)
  cmake_policy(SET CMP0042 NEW)
endif()

if(NOT EXISTS ${PROJECT_SOURCE_DIR}/bin)
  file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
endif()
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/lib)
  file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
endif()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)

include_directories(${PROJECT_SOURCE_DIR}/include)
add_library(complex SHARED ${PROJECT_SOURCE_DIR}/src/Complex.cpp)
set_property(TARGET complex PROPERTY POSITION_INDEPENDENT_CODE ON)
message(STATUS "The Complex.cpp will be compiled as a shared library")

add_executable(HelloComplex
  ${PROJECT_SOURCE_DIR}/src/HelloComplex.cpp)
target_link_libraries(HelloComplex complex)
add_test(exeTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/HelloComplex)
install(TARGETS complex DESTINATION lib)
install(TARGETS HelloComplex DESTINATION bin)

Some explanations on the new contents compared to Chapter 1:

  1. set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install) sets the ${CMAKE_INSTALL_PREFIX} to ${PROJECT_SOURCE_DIR}/install, i.e. the install/ folder in the main project folder. If it doesn’t exist, create one. This is what we talked above, about CMake installation
  2. cmake_policy(SET CMP0042 NEW) was specially added for OSX. This gets rid of the warning CMake gives on OSX about shared libraries.
  3. add_library(complex SHARED ${PROJECT_SOURCE_DIR}/src/Complex.cpp) creates a shared library using the Complex.cpp file.
  4. set_property(TARGET complex PROPERTY POSITION_INDEPENDENT_CODE ON) sets the property POSITION_INDEPENDENT_CODE of target complex (the library) to ON. This is equivalent to specifying -fPIC in GNU C/C++ compilers. See my post about C/C++ libraries
  5. message(STATUS "...") prints the message on the screen.
  6. target_link_libraries(HelloComplex complex) is self-explanatory: target executable HelloComplex depends on library complex
  7. install(TARGETS ... DESTINATION ...) will put the targets (executable and library) to the DESTINATION folders, i.e. ${CMAKE_INSTALL_PREFIX}/path_given_by_DESTINATION}.

The full contents of this chapter can be downloaded through the zipped complete project folder:
cmake_tutorial_chapter2

2 comments